vue2.0_路由守卫——登录权限设置( 以vue router.beforeEach()为例 )

前言: 什么是路由守卫?

官方的说法:

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

我方的说法:

路由守卫是指对路由跳转前做一些验证或者说操作一些事儿,目的是为了实现某些效果。比如没有登录的情况下,就不能进入到首页或者其它页面,只有登录了之后才有权限进入。简单讲就是路由拦截设置权限

讲解前:为了方便理解,我先贴一张钩子函数的执行顺序图。先讲解一下路由守卫的组成和各个钩子的理解与应用,登录权限设置放在最后。

在这里插入图片描述

路由守卫的分类

全局的守卫(3类)

理解什么是全局的守卫:是指路由实例上直接操作的钩子函数,特点是所有路由配置的组件都会触发,直白点就是触发路由就会触发这些钩子函数。

一、全局前置守卫[ beforeEach ] ——一般用来做一些进入页面的限制(比如最后面的登录权限设置)
import router from './router'

/**守卫方法接收三个参数:
 * @param {to} 即将要进入的目标路由对象
 * @param {from} 当前导航正要离开的路由对象
 * @param {next} 执行下一步
*/
/**next函数的几种用法:
 * next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
 * next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。
 * next(false): 中断当前的导航。
 * next(error): 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
*/
router.beforeEach((to, from, next) => {
    document.title = to.meta.title || '全城购';
    if (to.meta.needLogin && !$store.state.isLogin) {
      next({
        path: '/login'
      })
    }
    else {
      next()
    }
  })
注意:要确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。
举例:
//错误例子
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) {
	next({ name: 'Login' })
  }
  // 如果用户未能验证身份,则 `next` 会被调用两次
  next()
})
//正确例子
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) {
	next({ name: 'Login' })
  } 
  else {
	next()
  }
})
二、全局解析守卫[ beforeResolve ]

理解:这个钩子和beforeEach类似,也是路由跳转前触发,参数也是to,from,next三个,和beforeEach区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。即在 beforeEach 和 组件内beforeRouteEnter(组件内的守卫) 之后,afterEach之前调用。

三、全局后置钩子[ afterEach ]——少用

理解:全局后置钩子与全局前置守卫类似,然而和守卫不同的是,该钩子不会接受 next 函数也不会改变导航本身。和beforeEach相反,它是在路由跳转完成后触发,参数包括to,from没有了next,它发生在beforeEach和beforeResolve之后,beforeRouteEnter(组件内的守卫)之前。

 router.afterEach((to, from) => {
      // ...
   })

单个路由独享守卫(beforeEnter)

理解:单个路由独享守卫是指在单个路由配置的时候也可以设置的钩子函数,其位置就是下面示例中的位置,也就是像Foo这样的组件都存在这样的钩子函数。它和beforeEach完全相同,如果都设置则在beforeEach之后紧随执行,参数to、from、next。

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

注:它与全局前置守卫的方法参数是一样的。
不同的是:全局守卫可以作用于全局,路由独享守卫只作用于被设置守卫的路由。

组件内的守卫

理解:是指在组件内执行的钩子函数,类似于组件内的八大生命周期,相当于为配置路由的组件添加新的生命周期钩子函数。

一、进入组件路由前的守卫[ beforeRouterEnter —— 此时组件中this的指向并不是该组件实例 ]

理解:路由进入之前调用,参数包括to,from,next。该钩子在全局守卫beforeEach和独享守卫beforeEnter之后,全局beforeResolve和全局afterEach之前调用,要注意的是该守卫内访问不到组件的实例,也就是this为undefined,也就是它在beforeCreate生命周期前触发。在这个钩子函数中,可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数,可以在这个守卫中请求服务端获取数据,当成功获取并能进入路由时,调用next并在回调中通过 vm访问组件实例进行赋值等操作,(next中函数的调用在mounted之后:为了确保能对组件实例的完整访问)

<template>
  ...
</template>
export default{
  name:'****'
  data(){
    return{}
  },
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  mounted(){},
  methods:{}
}
<style>
  ...
</style>
二、路由改变时的守卫[ beforeRouteUpdate ]

理解:在当前路由改变时,并且该组件被复用时调用,可以通过this访问实例。参数包括to,from,next。

<template>
  ...
</template>
export default{
  name:'****'
  data(){
    return{}
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  mounted(){},
  methods:{}
}
<style>
  ...
</style>
三、离开组件路由前的守卫[ beforeRouteLeave ]

理解:导航离开该组件的对应路由时调用,可以访问组件实例this,参数包括to,from,next。

<template>
  ...
</template>
export default{
  name:'****'
  data(){
    return{}
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  },
  mounted(){},
  methods:{}
}
<style>
  ...
</style>

总结所有钩子:

全局路由钩子:beforeEach(to,from, next)、beforeResolve(to,from, next)、afterEach(to,from)
独享路由钩子:beforeEnter(to,from, next)
组件内路由钩子:beforeRouteEnter(to,from, next)、beforeRouteUpdate(to,from, next)、beforeRouteLeave(to,from, next)

补充

返回上一级路由或返回下一级路由的方法

//返回上一级
window.history.back();
window.history.go(-1);
this.$router.go(-1);
//返回下一级
window.history.go(1);
this.$router.go(1);

登录权限设置

第一步

在src目录下新建一个permission.js文件,在这个文件中要写的就是我们的全局前置守卫——登录权限设置。然后,将permission.js文件引入到main.js当中。

import './permission' // 登录权限设置
第二步

新建login、homePage和forgetPassword组件,然后在router中定义这三个路由

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter);

const routes = [
  {
    path: '/login',
    name: 'login',
    component: () => import('../views/login.vue')
  },
  {
    path: '/homePage',
    name: 'homePage',
    component: () => import('../views/homePage.vue')
  },
  {
    path: '/forgetPassword',
    name: 'forgetPassword',
    component: () => import('../views/forgetPassword.vue')
  },
];

const router = new VueRouter({
  routes
});

export default router

引入main.js中

import router from './router'
第三步

在permission.js文件中,引入router文件,并新建白名单

import router from './router'

//白名单:就是指不需要登录就可以直接进入的组件地址名单
const whiteList = []
第四步

引入获取登录返回的token方法,删除token的方法,使用router.beforeEach()

import router from './router'
import store from './store'//删除token的方法在vuex中
import {getToken} from '@/utils/auth'//获取token

//白名单:就是指不需要登录就可以直接进入的路由地址名单
const whiteList = ['/login','/forgetPassword']

//to:进入到哪个路由去
//from:从哪个路由离开
//next:进入下一步
router.beforeEach(async (to, from, next) => {
  //console.log('to.meta:', to.meta);
  if (to.meta) {
    document.title = to.meta.title;
    //以下判断和操作,大家根据实际的项目需求去做,这里做参考即可。
    try {
      // 获取token,判断用户是否登录
      const hasToken = getToken();
      console.log('hasToken:', hasToken)
      /* 如果有token*/
      if (hasToken) {
      	  // 如果已登录且路由地址在白名单中,则重定向到主页去
          if (whiteList.indexOf(to.path) !== -1) {
              next({path:'/homePage'});
          }
          // 如果已登录且路由地址不在白名单中,则直接进入该路由地址
          else {
         	 next()
          }
      }
      /* 没有token*/
      else {
        // 如果没有登录且路由地址在白名单当中,则直接进入该路由地址
        if (whiteList.indexOf(to.path) !== -1) {
          next()
        }
        // 如果没有登录且路由地址不在白名单当中,则重定向到登录页去
        else {
            next(`/login`)
        }
      }
    }
    catch (error) {
      // 删除token,然后跳转到登录页面重新登录。
      await store.dispatch('user/resetToken');
      Toast(error || 'Has Error');
      next(`/login`)
    }
  }
});

以上便是以vue router.beforeEach()为例的登录权限设置,这边我只是举个例子,所以判断的时候都是简单判断,大家根据在项目中的需求去判断即可,本例子仅作参考。


版权声明:本文为QiuRenBB原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。