Vue Router 导航守卫快速了解与上手应用

Posted 北极光之夜。

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue Router 导航守卫快速了解与上手应用相关的知识,希望对你有一定的参考价值。

一.简介与小案例:

  嘿,最近你还好吗?ヽ(。◕‿◕。)ノ゚。今天,就分享一下vue导航守卫的快速了解与应用,对这部分基础不了解的可以快速上手。

  在我们通过 vue-router 路由发生跳转时的,也可以用导航这一词概括。导航就是表示路由正在发生改变。而导航守卫就可以说成是监听这一改变。当路由跳转时不能直接跳转,会先经过导航守卫设定的条件,通过条件判断接下来该执行什么操作,给不给跳转,或者应该跳到哪里,或者其它操作。
   先不谈 vue 提供的导航守卫,假如我们要自己实现 ‘守卫’ 该怎么实现呢?下面有一个小案例,快速了解 ‘守卫’ 概念。

 1.首先新建了一个vue脚手架项目,并新建一个login.vue与user.vue文件,(假如login为自己定义的登录页,user为个人信息页)然后在路由配置中添加这两个文件的路径,最后在App.vue页面中添加对应的路由链接。以上,为路由的基本使用,就不演示了。最后运行效果如下:


其中,路由配置我是这样:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/login/login.vue'
import User from '../views/user/user.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path:'/login',
    component: Login
  },
  {
    path:'/user',
    component:User
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

 2. 现在,要实现守卫的概念。就是当我没有在登录页进行登录时,直接点击User时,路由不会跳转到user页面,会直接跳转到login登录页面,只有当我在登录页登录后,点击User才能跳转到user页面:

login.vue页面代码如下,本来登录是要向服务器发一次请求的,因为是演示,所以这里登录不会调用接口。那我们就假如登录后会把账号存储在浏览器本地存储中,当跳转到user页面时,user页面判断本地存储是否存在登录账号信息,存在则显示,否则强行跳到登录页:

<template>
  <div>
    <h3>这是登录页面,登录后才可查看个人信息页面</h3>
    <div>
      <div>账号:<input type="text" v-model="username" /></div>
      <div>密码:<input type="text" v-model="password" /></div>
      <button @click="signIn">登录</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: "",
      password: "",
    };
  },
  methods: {
    signIn() {
    // 保存账号信息
      let token = this.username;
      // 本地存储
      localStorage.setItem("token", token);
    },
  },
};
</script>
<style scoped>
</style>

使用 localStorage 创建一个本地存储的 name/value 对。

user.vue页面如下,在生命周期 created 阶段,进行判断是否登录:

<template>
  <div>
    <h2>个人信息页面</h2>
    <div>账号:{{ username }}</div>
    <div>博客:北极光之夜。</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: "",
    };
  },
  methods: {},
  created() {
  // 获取本地存储
    var token = localStorage.getItem("token");
    console.log(token);
    // 判断是否有登录信息
    if (token) {
       // 有则给username赋值
      this.username = token;
    } else {
    // 没有则路由跳转到登录页
      this.$router.push("/login");
    }
  },
};
</script>
<style scoped>
</style>

效果如下,没登录时,本地存储没信息,路由不会跳转到user页面,会直接跳转到login登录页面:


登录后,本地存储有值,可以跳转:






 以上,就基本演示了下守卫的概念,不过是比较繁琐的,假如有很多页面,我们不可能在每个页面都写一次获取和判断,所以我们要引入 vue 中导航守卫,快速实现守卫。

二.vue导航守卫:

导航守卫官方文档链接:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

1.全局前置守卫:

 全局前置守卫用于控制每一次路由跳转,叫做 router.beforeEach,可以使用 router.beforeEach 注册一个全局前置守卫。这个一般写在vue项目中的src文件夹下的 main.js 中:
我的main.js文件内容:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false

// 添加全局前置守卫
router.beforeEach((to,from,next)=>{
     console.log(to,from,next);
     next();
})

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

可以看到,写法如下:

router.beforeEach((to,from,next)=>{
     console.log(to,from,next);
     next();
})

看控制台输出,to,from,next代表什么:

看得出to、from都是对象,next为方法。还是直接上概念,其中to、from、next 参数意思如下:

参数意义
to代表将要跳到哪个路由
from代表之前是来自哪个路由
next()进行正常跳转
next(false)取消跳转
next(“路径”)或者next({ path: ‘路径’ })路由重定向,跳转到其它页面

 需要注意的是,要确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。

 如,我现在要实现无法跳转到login页面,其它页面能正常跳转,应该如下实现:

router.beforeEach((to,from,next)=>{
  //如果将要跳到login,则取消跳转
    if(to.path=='/login'){
      next(false);
    }else{
      // 否则正常跳转
      next();
    }
})

效果:


现在,用全局前置守卫实现第一大点那个小案例:

router.beforeEach((to,from,next)=>{
   if(to.path == '/user'){      
      if(localStorage.getItem("token")){
         next();
      }else{
        next('/login');
      }

   }else{
     next();
   }
})

看,也是一样可以的:


假如现在进入about页面也要登录才能进,则如下直接在 if 里添加一个或条件就好:

router.beforeEach((to,from,next)=>{
   if(to.path == '/user' || to.path == '/about'){      
      if(localStorage.getItem("token")){
         next();
      }else{
        next('/login');
      }

   }else{
     next();
   }
})

2. 路由守卫:

 运行在路由上的守卫,只有进入到当前路由页面的时候才会执行的守卫函数,叫做 beforeEnter。它一般直接在路由配置里写的。

如我在/user的配置对象里写,只有在user页面这个守卫在生效:

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path:'/login',
    component: Login
  },
  //  在user这里面写
  {
    path:'/user',
    component:User,
    beforeEnter: (to, from, next) => {
           // 输出看看
      console.log(to,from,next);
    }
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

看效果,只有在点击user页面才会有输出:


 这些守卫与全局前置守卫的方法参数是一样的。用法也是差不多一样的 。现在在路由守卫里实现第一大点的案例:

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path:'/login',
    component: Login
  },
  //  在user这里面写
  {
    path:'/user',
    component:User,
    beforeEnter: (to, from, next) => {
      if(localStorage.getItem('token')){
        next();
      }else{
        next('/login');
      }
    }
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

效果:

3.组件内守卫:

可以在路由组件内直接定义以下路由导航守卫:

名称调用时期
beforeRouteEnter进入组件之前执行
beforeRouteUpdate组件复用时执行
beforeRouteLeave离开组件之前执行

 它们也是函数,跟生命周期钩子函数有点像,跟data()、created()这些函数也是同一个级别的。它既然是写在组件里的,那说明它也是在该组件中有效的。比如在user.vue页面写:

beforeRouteEnter:

<template>
  <div>
    <h2>个人信息页面</h2>
    <div>账号:{{ username }}</div>
    <div>博客:北极光之夜。</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: "",
    };
  },
  // 进入组件之前调用,beforeRouteEnter,这个比较少用
  beforeRouteEnter(to, from, next) {
    console.log(to, from, next);
    next();
  },
  methods: {},
  created() {},
};
</script>
<style scoped>
</style>

 这些守卫与全局前置守卫的方法参数也是是一样的。现在我们同样实现第一大点的小案例,在这之前需要注意的是不能获取组件实例的this,因为当守卫执行前,组件实例还没被创建:

  // 进入组件之前调用
  beforeRouteEnter(to, from, next) {
    if (to.path == "/user") {
      if (localStorage.getItem("token")) {
        next();
      } else {
        next("/login");
      }
    }
  },

一样的效果:


beforeRouteLeave:

 接下来使用 beforeRouteLeave,它类似于生命周期函数的beforeDestroy,表示离开该组件时执行的守卫函数。离开就是说我要去其它页面了,比如跳去home页面,注意如果没有调用next将离不开该组件。方法参数也是一样的:

<script>
export default {
  data() {
    return {
      username: "",
    };
  },
  // 离开该组件时执行的守卫函数
  beforeRouteLeave(to, from, next) {
    next();
  },
  methods: {},
  created() {},
};
</script>

它有什么使用场景呢?

 比如因为用户误触使得要离开这个页面时,可以通过beforeRouteLeave函数跳出一个提示框,提示是否真的离开该页面(提示离开可能不会保存当前页面用户的输入的信息等)。实现如下:

<template>
  <div>
    <h2>个人信息页面</h2>
    <div>账号:{{ username }}</div>
    <div>博客:北极光之夜。</div>
    <div>输入1:<input type="text" /></div>
    <div>输入2:<input type="text" /></div>
    <div>输入3:<input type="text" /></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: "",
    };
  },
  // 离开该组件时执行的守卫函数
  beforeRouteLeave(to, from, next) {
    const r = window.confirm("那你走?输入信息将不会保留");
    if (r) {
      next();
    } else {
      next(false);
    }
  },
  methods: {},
  created() {},
};
</script>
<style scoped>
</style>

window.confirm() 方法方法用于显示一个带有指定消息和确认及取消按钮的对话框。如果访问者点击"确定",此方法返回true,否则返回false。

运行效果:

beforeRouteUpdate

 在组件复用时执行beforeRouterUpdate,什么是复用呢?当连续进入同一个动态路由页面时会产生复用。
 什么是动态路由呢?如果某些路由规则的一部分是一样的,只有另一部分是动态变化的,那我们可以把这些动态变化的部分形成路由参数,这些参数就叫做动态路由匹配。动态路由请看这篇文章第五点:https://auroras.blog.csdn.net/article/details/117635206,这里就不细说了。

首先,在项目新建一个 details.vue文件,做为详情页,然后配置动态路由规则:

  {
    path:'/details/:id',
    component: Details
  },

然后添加两个路由链接:

...略
      <router-link to="/details/1">详情1</router-link> |
      <router-link to="/details/2">详情2</router-link> |
...略      

 效果如下,此时连续进入就发生了组件的复用,它们显示的其实都是details.vue文件。因为是复用,vue为了节省性能将会导致该组件生命周期钩子函数并不会再重头开始创建执行,所以这时beforeRouteUpdate就很有用处了:


设置beforeRouteUpdate守卫函数,details.vue文件内容如下:

<template>
  <div>详情页</div>
</template>

<script>
export default {
  data() {
    return {};
  },
  // 守卫函数
  beforeRouteUpdate(to, from, next) {
    console.log("复用了~");
    next();
  },
  methods: {},
};
</script>
<style scoped>
</style>

look:


 现在,实现通过beforeRouteUpdate守卫函数,当组件发生复用时,依旧能获取组件参数(生命周期created函数是不行的,因为组件复用时它不会重新执行):

<template>
  <div>详情页 {{ id }}</div>
</template>

<script>
export default {
  data(<

以上是关于Vue Router 导航守卫快速了解与上手应用的主要内容,如果未能解决你的问题,请参考以下文章

vue-router有哪几种导航钩子( 导航守卫 )?

vue-router 导航守卫

vue-router 既不监视路由也不监视导航守卫开火

vue-router进阶-1-导航守卫

vue-router路由导航守卫

八Vue Router 进阶-导航守卫