所谓导航守卫,就是在当你进行导航的时候将其拦截下来,从而方便做一些其他的事情。
快速上手
// 全局导航守卫
router.beforeEach((to, from, next) => {
// 回调函数里面决定了拦截下来后做什么
console.log('from:', from)
console.log('to:', to)
console.log('导航到:', to.name)
next() // 调用该方法代表放行
})
这是一个全局导航守卫,回调会自动传入 3 个参数:
- to:即将要进入的目标路由,是一个对象,对象里面有 path、fullPath、hash、params 等参数
- from:当前导航正要离开的路由,同样是一个对象,对象内部有上述参数
- next:是一个函数,表示导航放行
各种拦截守卫
整体分为 3 大类:
- 全局守卫
- beforeEach:全局前置守卫,会在解析组件守卫和异步路由组件之前被调用
- beforeResolve:全局解析守卫,在导航被确认之前,但在所有组件内守卫和异步路由组件被解析之后调用
- 上面两个其实就是执行的时机一前一后
- afterEach:全局后置守卫,在导航确认后触发的钩子函数。该钩子函数执行后会触发 DOM 更新,用户看到新的页面。
- 思考🤔:既然导航都已经确认了,这里安插一个守卫干嘛呢?
- 全局后置守卫经常用于如下的场景:
- 记录页面访问历史:可以使用 afterEach 来记录用户访问的页面,以便进行统计或分析。
- 关闭加载指示器:在 beforeEach 中开启加载指示器,在 afterEach 中关闭它,以提供更好的用户体验。
- 页面切换动画:在 afterEach 中触发页面切换动画或其他视觉效果,以提升用户体验。
- 更新文档标题:在导航完成后更新页面标题,以反映当前页面内容。
- 路由守卫 beforeEnter:针对特定路由设置的守卫,因此设置的方式也不再是在 router 路由实例上面设置,而是在某个路由配置中设置。
const routes = [
{
path: '/users/:id',
component: UserDetails,
// 在路由的配置中进行设置,只针对特定的路由进行拦截
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
相关细节:
- beforeEnter 守卫只在进入路由时触发,不会在 params、query 或 hash 改变时触发。
- 从 /users/2 进入到 /users/3 这种不会触发
- 从 /users/2#info 进入到 /users/2#projects 这种也不会触发
- 如果放在父级路由上,路由在具有相同父级的子路由之间移动时,它不会被触发。
const routes = [
{
path: '/user',
beforeEnter() {
// ...
},
children: [
{ path: 'list', component: UserList },
{ path: 'details', component: UserDetails },
],
},
]
从 /user/list 跳转到 /user/details 不会触发路由级别守卫。
- 组件守卫:这种守卫是组件级别,取决于是否进入或者离开某个组件
- beforeRouteEnter:进入了该导航,组件开始渲染之前
- beforeRouteUpdate:当前路由改变,但是该组件被复用时调用,例如对于一个带有动态参数的路径 /users/:id,在 /users/1 和 /users/2 之间跳转的时候会触发
- beforeRouteLeave:离开了该导航,组件失活的时候
整体的执行顺序:
- 组件离开守卫
- 全局前置守卫
- 路由级别守卫
- 全局解析守卫
- 全局后置守卫
- 组件进入守卫
如果是组件复用,参数变化的情况,执行顺序如下:
- 全局前置守卫
- 组件更新守卫
- 全局解析守卫
- 全局后置守卫
其他细节
1. 路由级别守卫beforeEnter设置多个值
路由级别守卫,也就是 beforeEnter 可以设置成一个数组,数组里面包含多个方法,每个方法进行一项处理。
const routes = [
// ...
{
path: '/about',
name: 'About',
component: About,
beforeEnter: [
(to, from, next) => {
console.log('Route beforeEnter step 1')
next()
},
(to, from, next) => {
console.log('Route beforeEnter step 2')
next()
}
]
},
// ...
]
2. 在守卫内的全局注入
从 Vue 3.3 开始,你可以在导航守卫内使用 inject() 方法。这在注入像 pinia stores 这样的全局属性时很有用。
在 app.provide() 中提供的所有内容都可以在全局守卫里面获取到。
// main.js
const app = createApp();
app.provide('global', 'some data');
// router.js
router.beforeEach(()=>{
const data = inject('global');
const userStore = useUserStore();
})
实战案例
使用导航守卫拦截未登录的用户,将未登录用户导航到登录页面。
角色:普通用户、管理员
页面:主页、用户页、管理员页、登录
未登录:主页、登录
用户身份登录:主页、用户页、登录
管理员身份登录:主页、用户页、管理员页、登录