文章目录
- 前言
- 一、守卫类型概览
- 1.1 三种守卫
- 1.2 导航完整流程
- 二、全局守卫
- 2.1 beforeEach:全局前置守卫
- 2.2 afterEach:全局后置守卫
- 2.3 to / from 路由对象
- 三、Vue Router 4 导航控制
- 3.1 return 替代 next()
- 3.2 return 值的含义
- 3.3 异步守卫
- 四、路由独享守卫
- 4.1 beforeEnter
- 4.2 与全局守卫的分工
- 五、组件内守卫
- 5.1 三个 Composition API 守卫
- 5.2 onBeforeRouteLeave:离开确认
- 5.3 onBeforeRouteUpdate:同组件参数变化
- 5.4 onBeforeRouteEnter:组件未创建
- 六、典型场景
- 6.1 登录拦截 + 回跳
- 6.2 动态页面标题
- 6.3 NProgress 进度条
- 6.4 编辑页离开确认(组合式封装)
- 七、面试聚焦
- 7.1 Vue Router 4 推荐 return 替代 next()
- 7.2 onBeforeRouteEnter 能访问组件实例吗?
- 7.3 多个守卫的执行顺序
- 八、易混淆点
- 九、思考与练习
- 总结
前言
路由守卫是 Vue Router 在导航跳转各阶段提供的拦截机制,常用于登录校验、权限控制、离开确认等场景。本篇会讲清楚:
- 全局守卫、路由独享守卫、组件内守卫
- Vue Router 4 用
return控制导航 - 常见业务场景与完整示例
一、守卫类型概览
1.1 三种守卫
| 类型 | 注册位置 | 作用范围 |
|---|---|---|
| 全局守卫 | router.beforeEach/afterEach | 所有路由跳转 |
| 路由独享守卫 | 路由配置beforeEnter | 单个路由 |
| 组件内守卫 | 组件内onBeforeRouteXxx | 当前组件相关路由 |
1.2 导航完整流程
触发导航 ↓ beforeRouteLeave(离开的旧组件) ↓ beforeEach(全局前置) ↓ beforeEnter(目标路由独享) ↓ beforeRouteEnter(进入的新组件) ↓ 解析异步组件 / 执行路由组件守卫 ↓ beforeRouteUpdate(同一组件,路由参数变化) ↓ 导航确认 ↓ afterEach(全局后置) ↓ DOM 更新二、全局守卫
2.1 beforeEach:全局前置守卫
// router/index.jsimport{createRouter,createWebHistory}from'vue-router'constrouter=createRouter({history:createWebHistory(),routes:[{path:'/login',component:()=>import('@/views/Login.vue')},{path:'/admin',component:()=>import('@/views/Admin.vue'),meta:{auth:true}}]})router.beforeEach((to,from)=>{consttoken=localStorage.getItem('token')// 需要登录但未登录 → 重定向if(to.meta.auth&&!token){return{path:'/login',query:{redirect:to.fullPath}}}})2.2 afterEach:全局后置守卫
router.afterEach((to,from)=>{// 设置页面标题document.title=to.meta.title||'默认标题'// 页面访问埋点trackPageView(to.path)})注意:afterEach不接受return值,无法阻止或重定向导航,适合做副作用(标题、埋点、进度条结束)。
2.3 to / from 路由对象
router.beforeEach((to,from)=>{console.log(to.path)// '/user/1'console.log(to.params)// { id: '1' }console.log(to.query)// { tab: 'profile' }console.log(to.meta)// { auth: true, title: '用户' }console.log(to.fullPath)// '/user/1?tab=profile'console.log(to.name)// 'User'})三、Vue Router 4 导航控制
3.1 return 替代 next()
Vue Router 3 使用next()回调,Vue Router 4 推荐通过返回值控制导航:
// ❌ Vue Router 3router.beforeEach((to,from,next)=>{if(!token)next('/login')elsenext()})// ✅ Vue Router 4router.beforeEach((to,from)=>{if(!token)return'/login'// 重定向// return false // 取消导航// return { name: 'Login' } // 命名路由重定向// return undefined / true // 放行(默认)})3.2 return 值的含义
| 返回值 | 效果 |
|---|---|
undefined/true | 放行,继续导航 |
false | 取消当前导航 |
| 路由路径字符串 | 重定向到该路径 |
路由位置对象{ path, name, params, query } | 重定向到指定位置 |
3.3 异步守卫
router.beforeEach(async(to,from)=>{if(to.meta.auth){constisValid=awaitcheckToken()if(!isValid)return'/login'}})守卫可以返回 Promise,Vue Router 会等待 Promise resolve 后再继续导航。
四、路由独享守卫
4.1 beforeEnter
仅对当前路由生效,在beforeEach之后、beforeRouteEnter之前执行。
constroutes=[{path:'/admin',component:()=>import('@/views/Admin.vue'),meta:{roles:['admin']},beforeEnter:(to,from)=>{constrole=localStorage.getItem('role')if(!to.meta.roles.includes(role)){return'/403'}}},{path:'/user/:id',component:()=>import('@/views/User.vue'),beforeEnter:(to,from)=>{// 校验 id 格式if(!/^\d+$/.test(to.params.id)){return'/404'}}}]4.2 与全局守卫的分工
// 全局:登录态校验(所有需 auth 的路由)router.beforeEach((to,from)=>{if(to.meta.auth&&!token)return'/login'})// 独享:特定路由的角色/参数校验{path:'/admin',beforeEnter:(to,from)=>{if(role!=='admin')return'/403'}}五、组件内守卫
5.1 三个 Composition API 守卫
import{onBeforeRouteEnter,onBeforeRouteUpdate,onBeforeRouteLeave}from'vue-router'| 守卫 | 触发时机 |
|---|---|
onBeforeRouteEnter | 进入该组件对应路由前(组件尚未创建) |
onBeforeRouteUpdate | 路由变化但复用同一组件时(如/user/1→/user/2) |
onBeforeRouteLeave | 离开该组件对应路由前 |
5.2 onBeforeRouteLeave:离开确认
<script setup> import { ref } from 'vue' import { onBeforeRouteLeave } from 'vue-router' const isDirty = ref(false) onBeforeRouteLeave((to, from) => { if (isDirty.value) { const answer = window.confirm('有未保存的修改,确定离开?') if (!answer) return false // 取消导航 } }) </script>5.3 onBeforeRouteUpdate:同组件参数变化
<script setup> import { onBeforeRouteUpdate } from 'vue-router' const fetchUser = async (id) => { /* ... */ } onBeforeRouteUpdate(async (to, from) => { // /user/1 → /user/2,组件复用,需重新拉数据 await fetchUser(to.params.id) }) </script>也可用watch(() => route.params.id, fetchUser)替代,效果类似。
5.4 onBeforeRouteEnter:组件未创建
<script setup> import { onBeforeRouteEnter } from 'vue-router' onBeforeRouteEnter((to, from) => { // ❌ 无法访问组件实例(组件尚未创建) // ❌ 无法使用 setup 中定义的 ref / 方法 // ✅ 可在进入前做数据预取 // return fetchUser(to.params.id) // 返回 Promise,导航会等待 }) </script>Vue Router 4 变化:不再使用next(vm => ...)回调访问实例。进入后需在onMounted或通过watch route处理组件内逻辑。
六、典型场景
6.1 登录拦截 + 回跳
router.beforeEach((to,from)=>{consttoken=localStorage.getItem('token')if(to.path==='/login')returntrueif(to.meta.auth&&!token){return{path:'/login',query:{redirect:to.fullPath}}}})// Login.vue 登录成功后constroute=useRoute()constrouter=useRouter()router.push(route.query.redirect||'/')6.2 动态页面标题
router.afterEach((to)=>{document.title=to.meta.title?`${to.meta.title}- 我的应用`:'我的应用'})6.3 NProgress 进度条
importNProgressfrom'nprogress'router.beforeEach(()=>{NProgress.start()})router.afterEach(()=>{NProgress.done()})6.4 编辑页离开确认(组合式封装)
// composables/useLeaveConfirm.jsimport{ref}from'vue'import{onBeforeRouteLeave}from'vue-router'exportfunctionuseLeaveConfirm(){constisDirty=ref(false)onBeforeRouteLeave(()=>{if(isDirty.value){returnwindow.confirm('有未保存的修改,确定离开?')}})return{isDirty}}七、面试聚焦
7.1 Vue Router 4 推荐 return 替代 next()
// VR4:return '/login' 重定向,return false 取消router.beforeEach((to,from)=>{if(!token)return'/login'})7.2 onBeforeRouteEnter 能访问组件实例吗?
不能。组件尚未创建,无法访问 setup 中的 ref、methods。数据预取可返回 Promise;进入后的逻辑放在onMounted或watch route。
7.3 多个守卫的执行顺序
离开旧组件 → 全局 beforeEach → 路由 beforeEnter → 进入新组件 beforeRouteEnter → beforeRouteUpdate → 全局 afterEach。
任一守卫return false或重定向,后续守卫按规则中断或转向新目标。
八、易混淆点
- afterEach 不能阻止导航:只做副作用,无
return控制。 - onBeforeRouteEnter 无组件实例:Vue Router 4 已移除
next(vm => ...),勿与 VR3 混用。 - 全局守卫按注册顺序执行:多个
beforeEach依次执行。 - beforeRouteUpdate vs watch route:同组件参数变化时两者都可用;守卫在导航确认前执行,适合拦截;watch 适合响应式更新。
- meta 需主动配置:守卫里
to.meta.auth要在路由配置中预先声明。
九、思考与练习
1.路由守卫有哪些类型?
解析:全局守卫(beforeEach / afterEach)、路由独享守卫(beforeEnter)、组件内守卫(onBeforeRouteEnter / Update / Leave)。
2.Vue Router 4 如何取消或重定向导航?
解析:return false取消;return '/path'或return { name: 'Xxx' }重定向;不 return 或 return true 放行。
3.onBeforeRouteEnter 中为什么不能访问组件实例?
解析:守卫执行时组件尚未创建,setup 尚未运行,因此无法访问 ref、methods 等。
4.beforeEach 和 beforeEnter 如何分工?
解析:beforeEach 做全局通用逻辑(如登录态);beforeEnter 做单个路由特有校验(如角色权限、参数格式)。
5.如何实现编辑页离开时的未保存提示?
onBeforeRouteLeave(()=>{if(isDirty.value&&!confirm('确定离开?'))returnfalse})总结
- 三种守卫:全局、路由独享、组件内,在导航流程不同阶段拦截
- Vue Router 4:用
return控制导航,替代 Vue Router 3 的next() - 全局前置:登录校验、权限拦截;全局后置:标题、埋点、进度条
- 组件内:Leave 做离开确认,Update 处理同组件参数变化,Enter 无法访问实例
- 执行顺序:离开 → beforeEach → beforeEnter → Enter → Update → afterEach