05月26, 2021

Vue实战(十四)-- 导航守卫

十四、导航守卫

我们的项目差不多快要结束了,现在就剩下最后一个问题,我们有了Personal.vue这么一个页面,随之而来的就需要思考一个不容易想到的重要问题了:

Personal页面是属于每个用户自己的页面,在没有登录的时候是不允许访问的,里面有一些重要的,私密的数据。也就是说你没有登录,那就根本不允许你访问这个页面

但是现在,如果我们直接在url中输入 http://127.0.0.1:8080/personal,是直接能够访问到这个页面的

所以,我们的目的就是,没有登录,就不应该等通过url地址访问到这个页面,具体要怎么做,如下图:

页面引导示意图

一般情况下,我们需要在中间加入一个鉴权页面,这个页面对我们到底有没有权限访问到要访问的页面进行判断跳转,当然这个中间页面其实还有一个更加重要的目的:在进行导航的时候,是根据url地址来进行判断的,当页面刷新,第一次通过url进入的时候,如果你用到了仓库远程访问,这个时候信息回馈的还没有那么及时,不能判断仓库中是否已经存在值,所以这个中间鉴权页面还有一个功能就是等待异步数据的加载,然后再进行导航

概念

导航守卫又称路由守卫,实时监控路由跳转时的过程,在路由跳转的各个过程执行相应的操作,类似于生命周期函数,在开发过程中经常被使用,比如用户点击一个页面,如果未登录就调到登录页面,已登录就让用户正常进入。

实验步骤

1、导航配置中加入必须的标志

比如,这里的/personal导航是需要我们验证的,所以在/personal导航中加入判断标志,目的是为了其他不需要验证的导航就可以直接通过

{
    path:"/personal",
    name:"Personal",
    component:()=>import('@/views/Personal'),
    meta:{
        auth:true
    }
},

这里其实就是给导航的meta标志中加入了一个auth的标识值,在导航守卫的时候,直接判断这个值,如果有就需要导航守卫

2、加入鉴权页面,并配置导航

加入一个Auth.vue页面,这个页面主要是为了等待异步数据,然后实现导航效果

<template>
    <Center>
        <h1>正在登录中......</h1>
    </Center>
</template>

<script>
import {mapState} from 'vuex';
import Center from '@/components/Center.vue'

export default {
  components: { Center },
  computed: {...mapState("userStore",["user","isLoading"])},
  methods: {
    handleLogin(){
      if(this.isLoading){
        return;
      }
      if(this.user){
        this.$router.push({name:"Personal"})
      }else{
        this.$router.push({name:"Login"})
      }
    }
  },
  watch: {
    user:{
      immediate:true,
      handler(){
        this.handleLogin();
      }
    },
    isLoading:{
      immediate:true,
      handler(){
        this.handleLogin();
      }
    },
  },
}
</script>
<style></style>

配置导航数据:

{
    path:"/auth",
    name:"Auth",
    component:()=>import('@/views/Auth')
},

3、配置导航

在导航的index.js中加入下面的配置

router.beforeEach(function(to, from, next) {
  console.log("to", to);
  console.log("from", from);
  console.log("====>" + store.state.userStore.isLoading);
  console.log("====>" + store.state.userStore.user);
  if (to.meta.auth) {
    if (store.state.userStore.isLoading) {
      next({ name: "Auth" });
    } else if (store.state.userStore.user) {
      next(); //允许进入
    } else {
      next({ name: "Login" });
    }
  } else {
    next();
  }
});

这里配置了一个全局前置守卫

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。

每个守卫方法接收三个参数:

  • to: Route: 即将要进入的目标 路由对象

  • from: Route: 当前导航正要离开的路由

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
    • next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。

当然,不论是这里的参数也好,还是守卫方法也好,还有很多,这里做一下简单介绍。有兴趣的同学可以自行了解

路由守卫分类

全局路由一共分为三类:全局守卫,路由独享的守卫,组件内的守卫。

1、全局守卫

全局守卫有三种:

1.router.beforeEach(全局前置守卫)
就上面的例子来说,用户在未登录的时候进入Personal页面,我们就让用户跳转到登录页面,在已登录的时候让用户正常跳转到点击的Personal页面。
2.router.beforeResolve(全局解析守卫)
和全局前置守卫类似,区别是在跳转被确认之前,同时在所有组件内守卫和异步路由组件都被解析之后,解析守卫才调用。
3.router.afterEach(全局后置守卫)
只接受to和from,不会接受 next 函数也不会改变导航本身

2、路由独享守卫

独享守卫只有一种:beforeEnter。 该守卫接收的参数与全局守卫是一样的,但是该守卫只在其他路由跳转至配置有beforeEnter路由表信息时才生效。 router配置文件内容:

{
path: '/about',
name: 'about',
component: about,
beforeEnter:(to,from,next)=>{
  console.log(to);
  console.log(from);
  next()
}

3、组件内守卫

组件内守卫一共有三个:

  • beforeRouteEnter,
  • beforeRouteUpdate,
  • beforeRouteLeave

三者分别对应:进入该路由时执行,该路由中参数改变时执行,离开该路由时执行。

<template>
  <div>关于页面</div>
</template>
<script>
  export default {
    name: "about",
    beforeRouteEnter(to, from, next) {
      //进入该路由时执行
    },
    beforeRouteUpdate(to, from, next) {
      //该路由参数更新时执行
    },
    beforeRouteLeave(to, from, next) {
      //离开该路由时执行
    }
  }
</script>
<style scoped>
</style>

完整导航流程。

这些路由甚至有进入的先后顺序,大致如下,大家做个了解就行:

  1. 导航被触发。
  2. 在失活的组件里调用离开守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

Vue路由跳转报错问题解决

-w792

这个报错的原因:使用新导航取消了从“/auth”到“/personal”的导航。

主要是由于导航守卫的连续跳转引起的问题,只需要在导航配置文件的index.js中加入下面的代码就好了

const originalPush = VueRouter.prototype.push;

VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch((err) => err);
};

-w853

本文链接:http://www.yanhongzhi.com/post/VueInAction-14.html

-- EOF --

Comments