我们在写Vue项目的时候,少不了要使用路由vue-router,而路由导航守卫是vue-router中一个非常重要的概念,也是非常重要的技巧。它能够很好地帮助开发者“监视”每一个要跳转的路由。
一、什么是路由导航守卫
导航守卫主要用来通过跳转或取消的方式守卫导航。简单来说路由导航守卫就是可以让我们对用户要跳转的路由做一次检查,符合条件的就放行,不符合条件则强制用户跳转至指定位置,说白了路由导航守卫是在路由跳转之前需要做的检查及操作。
分类
- 全局守卫
- 路由独享守卫
- 组件守卫
应用
- 在跳转到界面前, 进行用户权限检查限制(如是否已登陆/是否有访问路由权限);
- 在跳转到登陆界面前, 先判断用户是否登陆,用户没有登陆才进行显示。
二、全局守卫
全局守卫针对任意路由跳转都会生效,它又分为全局前置守卫和全局后置守卫。
1. 全局前置守卫(beforeEach)
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫resolve完之前一直处于等待中。
执行时机:初始化时执行以及每次路由跳转切换前执行。
使用场景:全局前置守卫是最常用的导航守卫,它主要作用于登录验证,获取用户权限信息等场景。
使用方式:可以使用router.beforeEach注册一个全局前置守卫。
const router = new VueRouter({ ... })
router.beforeEach((to, from,next) => {
// ...
//to: 即将要进入的目标路由对象(这个对象中包含name,params,meta等属性)
//from:当前导航正要离开的路由对象(这个对象中包含name,params,meta等属性)
// next():跳转下一个路由。必须要调用next方法来resolved这个钩子,只有全部钩子执行完了,导航的状态才是confirmed(确认的)。
})
2. 全局后置守卫(afterEach)
当跳转到了当前界面时需要执行的操作。
执行时机:初始化时执行以及每次路由跳转切换后执行。
使用场景:主要作用于分析、更改页面标题、声明页面等辅助功能场景。
注意事项:后置守卫钩子不会接受next函数也不会改变导航本身。
使用方式:可以使用router.afterEach注册一个全局后置守卫。
const router = new VueRouter({ ... })
router.afterEach((to, from) => {
document.title = to.meta.title //修改网页的title
})
三、路由独享守卫(beforeEnter)
顾名思义就是只针对当前路由生效,和其他路由没有关系。
执行时机:只在进入当前路由时触发,不会在 params、query 或hash改变时触发。它只有在从一个不同的路由导航跳转时,才会被触发。
注意事项:路由独享守卫没有后置守卫,但是可以和全局后置守卫搭配使用。
使用方式:直接在路由配置上使用beforeEnter定义独享守卫。
const routes = [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from,next) => {
if(to.meta.isAdmin) {
next()
}else {
alert('暂无权限')
}
},
},
]
四、组件内守卫
在组件内直接定义路由导航守卫,组件内守卫又分为进入守卫,更新守卫(2.2新增),离开守卫。
1. 进入守卫(beforeRouteEnter)
执行时机:通过路由规则进入该组件时触发。
注意事项:进入守卫不能访问this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
使用方式:可以使用beforeRouteEnter注册一个进入守卫。
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !因为当守卫执行时,组件实例还没被创建!
}
可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
2. 更新守卫(beforeRouteUpdate)
执行时机:在当前路由改变,但是该组件被复用时触发。
使用方式:直接在路由配置上使用beforeRouteUpdate定义独享守卫。
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
}
3. 离开守卫(beforeRouteLeave)
执行时机:通过路由规则离开该组件时触发。
使用场景:离开守卫通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回false来取消。
使用方式:可以使用beforeRouteLeave注册一个离开守卫。
beforeRouteLeave (to, from, next) {
const answer = alert('还未保存,确定要离开吗')
if (answer) {
next() //放行
}else {
return false //取消
}
}
五、完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用beforeRouteLeave守卫。
- 调用全局的beforeEach守卫。
- 在重用的组件里调用beforeRouteUpdate守卫(2.2+)。
- 在路由配置里调用beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用beforeRouteEnter。
- 调用全局beforeResolve守卫 (2.5+)。
- 导航被确认。
- 调用全局的afterEach钩子。
- 触发DOM更新。
- 调用beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。
六、实战bug解析
面试场景:如何实现登陆后, 自动跳转到前面要访问的路由界面?
- 在全局前置守卫中, 强制跳转到登陆页面时携带目标路径的redirect参数;
if (userInfo.name) {
next()
} else {
// 如果还没有登陆, 强制跳转到login
next('/login?redirect='+to.path) // 携带目标路径的参数数据
}
- 在登陆成功后, 跳转到redirect参数的路由路径上。
await this.$store.dispatch('login', {mobile, password})
// 成功了, 跳转到redirect路由 或 首页
const redirect = this.$route.query.redirect
this.$router.replace(redirect || '/')
总结
独享守卫beforeEnter只有前置没有后置,可以搭配全局后置守卫afterEach使用。
进入守卫beforeRouteEnter是支持给next传递回调的唯一守卫。
组件内守卫与全局守卫的区别:
全局守卫是进入前和进入后,组件内守卫是进入前和出来后,组件守卫也可以搭配全局后置守卫afterEach使用。
路由守卫使用场景:
全局前置守卫beforeEach通常用来进行路由跳转的一些信息判断,判断是否登录,是否拿到对应的路由权限等等;
全局后置守卫afterEach对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用;
离开守卫beforeRouteLeave通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消。
每个守卫方法接收3个参数:
to(route):即将要进入的目标路由;
from(route):当前导航正要离开的路由;
next(function):一定要调用该方法来resolve这个钩子。执行效果依赖next方法的调用参数。