文章目录
- 前言
- 一、守卫类型概览
-
- [1.1 三种守卫](#1.1 三种守卫)
- [1.2 导航完整流程](#1.2 导航完整流程)
- 二、全局守卫
-
- [2.1 beforeEach:全局前置守卫](#2.1 beforeEach:全局前置守卫)
- [2.2 afterEach:全局后置守卫](#2.2 afterEach:全局后置守卫)
- [2.3 to / from 路由对象](#2.3 to / from 路由对象)
- [三、Vue Router 4 导航控制](#三、Vue Router 4 导航控制)
-
- [3.1 return 替代 next()](#3.1 return 替代 next())
- [3.2 return 值的含义](#3.2 return 值的含义)
- [3.3 异步守卫](#3.3 异步守卫)
- 四、路由独享守卫
-
- [4.1 beforeEnter](#4.1 beforeEnter)
- [4.2 与全局守卫的分工](#4.2 与全局守卫的分工)
- 五、组件内守卫
-
- [5.1 三个 Composition API 守卫](#5.1 三个 Composition API 守卫)
- [5.2 onBeforeRouteLeave:离开确认](#5.2 onBeforeRouteLeave:离开确认)
- [5.3 onBeforeRouteUpdate:同组件参数变化](#5.3 onBeforeRouteUpdate:同组件参数变化)
- [5.4 onBeforeRouteEnter:组件未创建](#5.4 onBeforeRouteEnter:组件未创建)
- 六、典型场景
-
- [6.1 登录拦截 + 回跳](#6.1 登录拦截 + 回跳)
- [6.2 动态页面标题](#6.2 动态页面标题)
- [6.3 NProgress 进度条](#6.3 NProgress 进度条)
- [6.4 编辑页离开确认(组合式封装)](#6.4 编辑页离开确认(组合式封装))
- 七、面试聚焦
-
- [7.1 Vue Router 4 推荐 return 替代 next()](#7.1 Vue Router 4 推荐 return 替代 next())
- [7.2 onBeforeRouteEnter 能访问组件实例吗?](#7.2 onBeforeRouteEnter 能访问组件实例吗?)
- [7.3 多个守卫的执行顺序](#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:全局前置守卫
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const router = 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) => {
const token = localStorage.getItem('token')
// 需要登录但未登录 → 重定向
if (to.meta.auth && !token) {
return { path: '/login', query: { redirect: to.fullPath } }
}
})
2.2 afterEach:全局后置守卫
javascript
router.afterEach((to, from) => {
// 设置页面标题
document.title = to.meta.title || '默认标题'
// 页面访问埋点
trackPageView(to.path)
})
注意 :afterEach 不接受 return 值,无法阻止或重定向导航,适合做副作用(标题、埋点、进度条结束)。
2.3 to / from 路由对象
javascript
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 推荐通过 返回值 控制导航:
javascript
// ❌ Vue Router 3
router.beforeEach((to, from, next) => {
if (!token) next('/login')
else next()
})
// ✅ Vue Router 4
router.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 异步守卫
javascript
router.beforeEach(async (to, from) => {
if (to.meta.auth) {
const isValid = await checkToken()
if (!isValid) return '/login'
}
})
守卫可以返回 Promise,Vue Router 会等待 Promise resolve 后再继续导航。
四、路由独享守卫
4.1 beforeEnter
仅对当前路由生效,在 beforeEach 之后、beforeRouteEnter 之前执行。
javascript
const routes = [
{
path: '/admin',
component: () => import('@/views/Admin.vue'),
meta: { roles: ['admin'] },
beforeEnter: (to, from) => {
const role = 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 与全局守卫的分工
javascript
// 全局:登录态校验(所有需 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 守卫
javascript
import {
onBeforeRouteEnter,
onBeforeRouteUpdate,
onBeforeRouteLeave
} from 'vue-router'
| 守卫 | 触发时机 |
|---|---|
onBeforeRouteEnter |
进入该组件对应路由前(组件尚未创建) |
onBeforeRouteUpdate |
路由变化但复用同一组件时(如 /user/1 → /user/2) |
onBeforeRouteLeave |
离开该组件对应路由前 |
5.2 onBeforeRouteLeave:离开确认
vue
<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:同组件参数变化
vue
<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:组件未创建
vue
<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 登录拦截 + 回跳
javascript
router.beforeEach((to, from) => {
const token = localStorage.getItem('token')
if (to.path === '/login') return true
if (to.meta.auth && !token) {
return { path: '/login', query: { redirect: to.fullPath } }
}
})
javascript
// Login.vue 登录成功后
const route = useRoute()
const router = useRouter()
router.push(route.query.redirect || '/')
6.2 动态页面标题
javascript
router.afterEach((to) => {
document.title = to.meta.title
? `${to.meta.title} - 我的应用`
: '我的应用'
})
6.3 NProgress 进度条
javascript
import NProgress from 'nprogress'
router.beforeEach(() => {
NProgress.start()
})
router.afterEach(() => {
NProgress.done()
})
6.4 编辑页离开确认(组合式封装)
javascript
// composables/useLeaveConfirm.js
import { ref } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'
export function useLeaveConfirm() {
const isDirty = ref(false)
onBeforeRouteLeave(() => {
if (isDirty.value) {
return window.confirm('有未保存的修改,确定离开?')
}
})
return { isDirty }
}
七、面试聚焦
7.1 Vue Router 4 推荐 return 替代 next()
javascript
// 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. 如何实现编辑页离开时的未保存提示?
javascript
onBeforeRouteLeave(() => {
if (isDirty.value && !confirm('确定离开?')) return false
})
总结
- 三种守卫:全局、路由独享、组件内,在导航流程不同阶段拦截
- Vue Router 4 :用
return控制导航,替代 Vue Router 3 的next() - 全局前置 :登录校验、权限拦截;全局后置:标题、埋点、进度条
- 组件内:Leave 做离开确认,Update 处理同组件参数变化,Enter 无法访问实例
- 执行顺序:离开 → beforeEach → beforeEnter → Enter → Update → afterEach