导航守卫
vue-router提供的导航守卫,守护每一次跳转
其本质是函数,当导航发生变化时自动执行
导航守卫分为三个级别
- 全局导航守卫
- 单个路由独享的导航守卫
- 组件级别的导航守卫
全局导航守卫
可以使用router.beforeEach注册一个导航守卫,进入路由之前触发,当用户满足条件时,才可以进入导航,否则取消跳转。
我用到的一个导航守卫的案例是:当用户每次发送请求时都要从本地获取token然后,后端会判断token是否存在并判断token是否过期,这样本身就是一件非常麻烦的事情每次都要携带token带着数据向后端请求,所以我当时就在路由配置里定义了一个导航守卫,当每次发送请求先将请求拦截下来对其给其经过一个加工[也就是让每次的请求都携带上token再向后台发送]
接收三个参数
- to:要进入的目标路由对象
- from:从哪个路出发到达目标路由
- next:一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
- next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
- next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
- next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
- next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
案例演示
router.beforeEach((to,from,next)=>{
console.log('to输出对象',to);
console.log('from输出对象',from);
next()// next()方法是起一个放行作用,不加next()这个请求是发送不出去的
})
一个案例
router.beforeEach((to,from,next)=>{
if(to.name==='login'){
return next() //当路由验证到login的时候不需要判断有没有login,因为首次登录肯定没有login
}
const token=localStorage.getItem('token')
if(!token){
return next('/login')// 当没有login时直接让路由跳转到登录路由下
}
next()// 上面token规则验证有后对请求放行
})
当没有token时是这样的
当有toke时
导航守卫其实不只有前置导航守卫其实还有后置导航守卫
这些钩子函数不接受next()函数,也不会改变导航本身,用的相对较少
分享一个小案例来带过一下这个后置守卫
当vue写一本小说时用到上一章下一章时,页面可视区域最终会出现在页面底部用户每次切换章节都要把页面从上面拉下来这样的使用户体验堪称极差
那么用什么方法来保证每次页面进行路由跳转时可视区域都在顶部呢这里用到了路由后置守卫
router.afterEach((to, from) => {
window.scrollTo(0, 0)
})
路由独享的导航守卫
只有跳到当前的这个路由下才会执行导航守卫
这种导航守卫定义在路由规则下
{
path: '/',
name: 'login',
component: login,
beforeEnter: (to, from, next) => {
console.log('即将进入登录页面')
next()
}
}
组件独享导航守卫
可以在路由组件内直接定义以下路由导航守卫:
- beforeRouteEnter
- beforeRouteUpdate (2.2 新增)
- beforeRouteLeav
const Foo = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
需要注意,beforeRouteEnter组件内还访问不到this,因为该守卫执行前组件实例还没有被创建,需要传一个回调给 next来访问,例如:
beforeRouteEnter(to, from, next) {
next(target => {
if (from.path == '/classProcess') {
target.isFromProcess = true
}
})
}
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
beforeRouteLeave (to, from, next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
完整导航流程解读
- 导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入
路由元数据
在定义路由的时候后面可以跟一个meta字段。元数据可以作为自定义显示路由的标题
{
path: '/a',
name: 'a',
component: A,
meta:{title:'下章标题'}
}
可以在组件内获取
{{$route.meta.title}
效果展示
javascript这么获取
在路由导航中也可以拿到这个meta中的信息
元数据meta应用一通过路由的后置导航守卫+meta实现动态改变页面tag表标题
路由规则中如下定义
{
path: '/book',
name: 'book',
component: Book,
meta: {
title: '《机器学习》'
},
router.afterEach((to, from) => {
window.scrollTo(0, 0)
document.title = to.meta.title
})
元数据应用二在元数据内定义一个键用于权限验证
路由规则中的meta中定义随便一个用于定义当前用户权限的键,我这里定义的时requireAuth,这里用户需要权限的是true
{
path: '/users/:id(\\d+)',
name: 'userinfo',
component: UsersInfo,
meta: {
title: '用户详情页',
requireAuth:true,
}
},
在路由的前置导航中我定义的权限验证规则
router.beforeEach((to, from, next) => {
if(!to.meta.requireAuth){
return next()
}else{
return alert('您当前权限不足')
}
const token = localStorage.getItem('token')
if (!token) {
return next('/login') // 当没有login时直接让路由跳转到登录路由下
}
}
next()
})
但是这种权限的验证不推荐
元数据的应用三搭配路由后置导航实现动态改变导航的图标
在路由规则中定义meta,meta中定义要显示的图标
http://localhost:8080映射的是上级目录public,所以把meta中存放的ico文件放到public中,在定义icon的值时加上public映射的地址也就是http://localhost:8080
// meta定义内容展示如下
{
path: '/login',
name: 'login',
component: Login,
meta: {
title: '登录',
requireAuth: false,
icon:'http://localhost:8080/login.ico'
}
// 路由后置导航代码如下
router.afterEach((to, from) => {
window.scrollTo(0, 0)
document.title = to.meta.title
document.querySelector('link[rel="icon"]').href=to.meta.icon //这里是获取dom中link标签并且rel属性值为icon的标签把它的href值改为元数据meta中定义的图标路径
})