VueRouter 基础教程系列 🎉
什么是导航守卫 ?
导航是用来描述路由跳转链接的动作。而导航守卫则是用来控制路由是跳转还是取消。 导航守卫的作用范围有:“全局守卫”、“路由独享”、“组件级导航守卫”。
全局守卫
全局前置守卫 - beforeEach
当全局前置守卫没有执行完成时、导航(路由)是处于挂起等待状态。beforeEach 方法主要接收两个参数:
to: 目标地址,要跳转的地址。from: 来源地址,正要离开的地址。
beforeEach 方法通过接受返回值来控制路由是跳转还是取消跳转。
- 如果返回
true或undefined则继续跳转。 - 如果返回
false则取消跳转。 - 如果返回的是“路径地址”或“路径地址对象”,则跳转到目标地址。
router.beforeEach(async () => {
// canUserAccess() 返回 `true` 或 `false`
return await canUserAccess();
});
我们一般会在 beforeEach 钩子中进行:
- 用户权限校验。
- 用户登录态的校验。
如果在导航的过程中发生错误,则会调用
router.onError()事件监听回调。
■ 可选第三个参数 —— next()
在 VueRouter 3.x 及之前还可以使用
beforeEach(to, from, next)的第三个参数来控制路由的导航,但在最新的 RFC 中这种方式已经不被建议使用,因为它可能在使用不当时,存在不执行或多次执行的情况,另外通过方法的返回值来控制导航更符合方法的调用直觉。当然 VueRouter 依然支持这种使用方式:
router.beforeEach(to, from, next) {
return next(false);
return next(true);
return next('/user');
return next({path:User, params:{a:1}});
}
全局解析守卫 - beforeResolve
在路由导航被确认之前,所有异步组件被解析或异步路由组件被解析完成后执行。
它的使用方式与 beforeEach 完全相同,主要的区别就在于执行的时机不同,beforeResolve 的执行时机在 beforeEach 、beforeEnter、beforeRouteEnter 之后,但又在路由导航被确认之前执行。
beforeResolve 会在 afterEach 之前执行。
由于 beforeResolve 会在导航被确认之前执行,所以可以通过返回值类型或者是第三个参数 next 来控制路由跳转是否取消。
router.beforeResolve(async to => {
if (to.meta.requiresCamera) {
try {
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
// ... 处理错误,然后取消导航
return false
} else {
// 意料之外的错误,取消导航并把错误传给全局处理器
throw error
}
}
}
})
router.beforeResolve 是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。
全局后置守卫 - afterEach
afterEach 钩子会在路由导航被确认之后执行,所以无法通过返回值类型或者第三个参数 next() 来控制导航跳转还是取消。但是 afterEach(to, from, failure) 依然接收一个 failure 作为第三个参数。
通过 Failure 参数可以帮助我们判断导航是否存在故障。
router.afterEach((to, from, failure) => {
if (!failure) sendToAnalytics(to.fullPath)
})
全局后置守卫常用于:
- 更改页面标题
- 统计与分析用户信息
路由独享守卫
直接在路由配置项中定义 beforeEnter 守卫。
const routers = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: async (to, from) => {
return await canAccess(to.path);
}
}
]
beforeEnter 只会在路由被第一次匹配时触发,不会在 params、query、hash 发生改变后触发。也就是说路由独享守卫只会触发一次。
例如第一次匹配并进入 /users/2 触发了路由独享守卫,此时再访问 /users/3 或者 /users/4?q=1#2 都不会再触发 beforeEnter 守卫。
虽然 beforeEnter 可以很直观的控制当前路由的导航,但是通常为了方便统一的代码管理,我们会使用 meta + beforeEach 来替代类似的行为。
组件级守卫
直接在路由组件内定义“组件级的守卫”。
beforeRouteEnter: 当路由进入组件,在渲染该组件的对应路由被验证前调用。beforeRouteUpdate: 当路由发生更新,在当前路由改变,但是该组件被复用时调用。beforeRouteLeave: 离开当前路由。
由于它们都是在路由导航被确认前执行,因此都会接收
to、from与next参数。
beforeRouteEnter
需要注意的是,该钩子触发时,组件还未渲染,所以无法获取组件实例 this。好在,该钩子的第三个参数可以接收一个回调函数,当组件被渲染,实例被创建时,便会执行回调函数,并将实例本身作为参数传递到回调函数中。
{
beforeRouteEnter(to, from, next) {
next(vm => {
console.log(vm);
})
}
}
主意:路由发生改变,但是路由组件被复用的情况下,
beforeRouteEnter不会被触发。
beforeRouteUpdate
在“动态路由”的匹配方式下,为了减少性能上的开销,路由组件会被复用,从而导致 beforeRouteEnter 只会触发一次,对于符合条件的路径参数匹配 beforeRouteUpdate 可以保证每次都会触发。
beforeRouteLeave
通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消
beforeRouteLeave (to, from) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (!answer) return false
}
导航守卫的执行流程
首先,我们要有一个认识,路由的守卫(钩子)永远要早于组件钩子的执行。
下面,通过一个表格按类别介绍路由守卫的执行顺序。
| Mounting | Updating | UnMounted |
|---|---|---|
| 1. beforeEach | ||
| 2. beforeEnter | ||
| 3. beforeRouteEnter | 1.beforeRouteUpdate | 1.beforeRouteLeave |
| 4. beforeResolve | ||
| 5. afterEach |
下面通过一个流程图的方式来说明路由守卫的执行顺序。
注意:
beforeRouteEnter的next回调执行时期要晚于beforeCreate与created生命周期,但早于mounted生命周期。