vue-router | 你该了解的路由守卫(导航守卫)

4,169 阅读7分钟

之前我们曾说过用vue.js开发出来的是SPA单页应用,而我们的页面跳转是借助vue官方封装好的vue-router来实现路由跳转的,那么谈到路由跳转,我认为不得不提的就是路由守卫(导航守卫),那么今天我们就来就我个人的见解谈谈我对路由守卫的理解。

前言

在浏览器中的本地缓存分为两种(参照chrome浏览器):

  1. Local Storage --本地缓存: 通常情况下,存在本地缓存当中的数据,只要不是人为手动的去本地缓存中清除掉,是会一直存在于你的浏览器当中的,时效性是永久的;
  2. Session Storage -- 会话缓存: 当我们关闭当前页面(结束会话),那我们存储在会话缓存中的数据就会被清除,时效性仅仅是当前会话进行时。

大家跟我来想象一个场景,我们在访问我们学校的教务系统的时候,每一次都要登录,哪怕你在你电脑打开了你们学校官网的教务系统,你讲url地址复制,在另一台电脑输入这个url地址,你也不能跳转到教务系统的页面去,而可能会直接跳转到登录页面,那么具体是如何实现的呢?

什么是导航守卫(路由守卫)?

航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。

通俗来讲:路由守卫就是路由跳转过程中的一些生命周期函数(钩子函数),我们可以利用这些钩子函数帮我们实现一些需求。

路由守卫又具体分为全局路由守卫独享守卫以及组件路由守卫

全局路由守卫

全局路由守卫有三种:

  1. 全局前置守卫:router.beforeEach
  2. 全局解析守卫:router.beforeResolve
  3. 全局后置守卫: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两个参数打印出来的两个对象。

image.png

如果我们将会话结束,重新在导航栏里输入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)

image.png

浅析完全局路由守卫之后我们来分析分析独享守卫,那么独享守卫有什么应用场景呢?大部分的电商平台的应用的购物车页面就采用的独享守卫,就是当我们路由跳转到购物车页面,其独享守卫就会进行判断,如果当前你已经登陆了,就能进入购物车的详情页,否则就跳转到登录界面。

路由独享守卫

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
})

这样我们只有在路由跳转到购物车的详情页时才会触发独享守卫,进行登录判断,其他页面都可以直接访问。

那组件内的路由守卫呢?在百度的首页你能发现一个你的关注的一个组件,如果我们没有登录就无法查看你账号的关注情况,并且会提醒你进行登录操作,那么是如何实现的呢?

image.png

组件路由守卫

其是指组件内的钩子函数,类似于组件内的生命周期,钩子函数执行的顺序包括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的一些个人理解,文章仅用于交流学习,有任何不足或补充欢迎大家留言评论,同共进步,谢谢!

点赞.jpg