之前我们曾说过用vue.js开发出来的是SPA单页应用,而我们的页面跳转是借助vue官方封装好的vue-router来实现路由跳转的,那么谈到路由跳转,我认为不得不提的就是路由守卫(导航守卫),那么今天我们就来就我个人的见解谈谈我对路由守卫的理解。
前言
在浏览器中的本地缓存分为两种(参照chrome浏览器):
- Local Storage --本地缓存: 通常情况下,存在本地缓存当中的数据,只要不是人为手动的去本地缓存中清除掉,是会一直存在于你的浏览器当中的,时效性是永久的;
- Session Storage -- 会话缓存: 当我们关闭当前页面(结束会话),那我们存储在会话缓存中的数据就会被清除,时效性仅仅是当前会话进行时。
大家跟我来想象一个场景,我们在访问我们学校的教务系统的时候,每一次都要登录,哪怕你在你电脑打开了你们学校官网的教务系统,你讲url地址复制,在另一台电脑输入这个url地址,你也不能跳转到教务系统的页面去,而可能会直接跳转到登录页面,那么具体是如何实现的呢?
什么是导航守卫(路由守卫)?
航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。
通俗来讲:路由守卫就是路由跳转过程中的一些生命周期函数(钩子函数),我们可以利用这些钩子函数帮我们实现一些需求。
路由守卫又具体分为全局路由守卫、独享守卫以及组件路由守卫。
全局路由守卫
全局路由守卫有三种:
- 全局前置守卫:router.beforeEach
- 全局解析守卫:router.beforeResolve
- 全局后置守卫:router.afterEach
beforeEach
其中接收三个参数to、from、next,其主要作用就是用于登录验证;
// Login.vue
// 缓存登录状
sessionStorage.setItem('username',this.ruleForm.username)
// main.js
router.beforeEach((to,from,next) => { //路由跳转的前置钩子
// console.log(to,from);
if(!sessionStorage.getItem('username') && to.name !== 'login') {
next({name: 'login'}) // 路由名称
}else{
next()
}
})
通常情况下,我们会在vue项目的src文件夹下的main.js写以上代码,因为我在配置路由的时候设置了重定向,所以首页默认会跳转到/home页面,在登录界面如果登录了,就将用户的username存入会话缓存,我们登陆之后,直接在 http://localhost:8080/home 页面的控制台可以看到to、from两个参数打印出来的两个对象。
如果我们将会话结束,重新在导航栏里输入http://localhost:8080/home 会直接跳转到http://localhost:8080/login 登录界面,所以就实现了我们验证登录的效果。
beforeResolve
其中也接收三个参数to、from、next,并且这个钩子函数与beforeEach类似,也是路由跳转前触发,官方解释其与beforeEach的区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
即在 beforeEach 和 组件内beforeRouteEnter 之后,afterEach之前调用。
// main.js
router.beforeResolve((to,from,next) => {
console.log(to,from);
next()
})
打印结果同上,并且beforeEach和beforeResolve在执行时必须加上next()才能执行后续的钩子函数内部的操作。
注:以上两个钩子函数执行时,还在当前页面,未跳转到跳转的页面(比如还能获取到路由跳转前的某个dom结构,当然因为vue是以数据驱动页面,所以不建议我们获取dom结构进行操作)
afterEach
和beforeEach相反,它是在路由跳转完成后触发,参数包括to,from没有了next,它发生在beforeEach和beforeResolve之后。
// main.js
router.afterEach((to,from) => {
console.log(to,from);
if(!sessionStorage.getItem('username') && to.name !== 'login') {
router.push('/login')
}
})
控制台的执行结果仍和beforeEach、beforeResolve两个全局守卫一致,只不过它是在路由跳转之后触发的,可以获取到路由跳转之后的dom结构,而不能获取跳转之前的dom结构。
三个全局守卫的控制台输出结果如下图所示:(输出结果都一致,仅仅是触发事件的顺序是beforeEach-->beforeResolve-->afterEach)
浅析完全局路由守卫之后我们来分析分析独享守卫,那么独享守卫有什么应用场景呢?大部分的电商平台的应用的购物车页面就采用的独享守卫,就是当我们路由跳转到购物车页面,其独享守卫就会进行判断,如果当前你已经登陆了,就能进入购物车的详情页,否则就跳转到登录界面。
路由独享守卫
beforeEnter
因为独享守卫通常是某一个或几个页面独有的,所以我们通常会将路由独享守卫写在/src/router/index.js里;
// router/index.js
{
path: '/detail',
name: 'detail',
component: () => import('@/views/Detail.vue'),
beforeEnter: (to,from,next) => {
if(!sessionStorage.getItem('username')) {
alert('请先登录')
// next({name: 'login'})
router.push('/login')
} else {
next()
}
}
}
注:因为我们在index.js里我们使用脚手架(vue.cli)创建Vue项目时,
已经声明了router,所以我们可以使用 router.push('/')跳转页面。
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
这样我们只有在路由跳转到购物车的详情页时才会触发独享守卫,进行登录判断,其他页面都可以直接访问。
那组件内的路由守卫呢?在百度的首页你能发现一个你的关注的一个组件,如果我们没有登录就无法查看你账号的关注情况,并且会提醒你进行登录操作,那么是如何实现的呢?
组件路由守卫
其是指组件内的钩子函数,类似于组件内的生命周期,钩子函数执行的顺序包括beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave三个,执行位置如下:
<template>
...
</template>
export default{
data(){
//...
},
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
<style>
...
</style>
路由守卫的回调参数
to:目标路由对象;
from:即将要离开的路由对象;
next:它是最重要的一个参数,相当于佛珠的线,把一个一个珠子逐个串起来。以下注意点务必牢记:
1.但凡涉及到有next参数的钩子,必须调用next() 才能继续往下执行下一个钩子,否则路由跳转等会停止。
2.如果要中断当前的导航要调用next(false)。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from路由对应的地址。(主要用于登录验证不通过的处理)
3.当然next可以这样使用,next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。意思是当前的导航被中断,然后进行一个新的导航。可传递的参数与router.push中选项一致。
4.在beforeRouteEnter钩子中next((item)=>{})内接收的回调函数参数为当前组件的实例item,这个回调函数在生命周期mounted之后调用,也就是,他是所有导航守卫和生命周期函数最后执行的那个钩子。
5.next(error): 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
总结
以上就是笔者对于vue-router的一些个人理解,文章仅用于交流学习,有任何不足或补充欢迎大家留言评论,同共进步,谢谢!