十四、导航守卫
我们的项目差不多快要结束了,现在就剩下最后一个问题,我们有了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>
完整导航流程。
这些路由甚至有进入的先后顺序,大致如下,大家做个了解就行:
- 导航被触发。
- 在失活的组件里调用离开守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
Vue路由跳转报错问题解决
这个报错的原因:使用新导航取消了从“/auth”到“/personal”的导航。
主要是由于导航守卫的连续跳转引起的问题,只需要在导航配置文件的index.js中加入下面的代码就好了
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch((err) => err);
};
Comments