目录
一、路由守卫相关知识
1、概念
vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。
2、作用
对路由进行权限控制
3、分类
全局守卫、独享守卫、组件内守卫
二、全局守卫详解
全局守卫又分为全局前置守卫和全局后置守卫
1、全局前置守卫
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。
① 执行时间
初始化时执行,每次路由切换前执行
② 使用场景
全局前置守卫通常用来进行路由跳转的一些信息判断,判断是否登录,是否拿到对应的路由权限等等。
③ 使用方法
可以使用 router.beforeEach
注册一个全局前置守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from,next) => {
//to: 即将要进入的目标路由
//from:当前导航正要离开的路由
// next():进行下一个路由
})
2、全局后置守卫
① 执行时间
初始化时执行,每次路由切换后执行
② 使用场景
对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
③ 注意项
不会接受 next函数,也不会改变导航本身
④ 使用方法
可以使用 router.afterEach
注册一个全局后置守卫
const router = new VueRouter({ ... })
router.afterEach((to, from) => {
document.title = to.meta.title //修改网页的title
})
三、路由独享守卫详解
① 执行时间
独享守卫 只在进入路由时触发,不会在 params、query 或 hash 改变时触发。它们只有在 从一个不同的
路由导航时,才会被触发。
② 注意项
独享守卫没有后置,可以全局后置路由守卫搭配使用
③ 使用方法
直接在路由配置上使用 beforeEnter
定义独享守卫
const routes = [
{
path: '/cats',
component: Cats,
beforeEnter: (to, from,next) => {
if(to.meta.isAuth) {
next()
}else {
alert('暂无权限')
}
},
},
]
四、组件内守卫详解
组件内直接定义路由导航守卫
组件内守卫又分为进入守卫和离开守卫
1、进入守卫
① 执行时间
通过路由规则进入该组件时被调用
② 注意点
进入守卫不能访问this
,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
③ 使用方法
可以使用 beforeRouteEnter
注册一个进入守卫
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !因为当守卫执行时,组件实例还没被创建!
},
④ 可以传一个回调给next来访问组件实例
可以通过传一个回调给 next 来访问组件实例
。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
2、离开守卫
① 执行时间
通过路由规则离开该组件时被调用
② 使用场景
离开守卫通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消
③ 使用方法
可以使用 beforeRouteLeave
注册一个离开守卫
beforeRouteLeave (to, from, next) {
const answer = alert('还未保存,确定要离开吗')
if (answer) {
next() //放行
}else {
return false //取消
}
}
五、案例练习
知识补充
meta
:路由元信息,即程序员自定义信息,在路由规则中配置
{
name:"miaomiao",
path:'/cats',
component:Cats,
//路由元信息,是否需要权限
meta:{isAuth:true}
}
案例需求:dogs.vue、catsAbout.vue、OwnerAbout.vue三个路由组件需要权限展示
采用bootstrap样式,可以在public/index.html中引入bootstrap样式
router/index.js代码
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
import Cats from "../pages/Cats";
import Dogs from "../pages/Dogs";
import CatsAbout from "../pages/CatsAbout";
import OwnerAbout from "../pages/OwnerAbout"
const router = new VueRouter({
routes:[
{
name:"miaomiao",
path:'/cats',
component:Cats,
children:[
{
name:"miaoAbout",
path:'catsAbout',
component:CatsAbout,
meta:{isCat:true}
},
{
name:"zhurenAbout",
path:'/ownerAbout',
component:OwnerAbout,
//路由元信息,是否需要权限
meta:{
isAuth:true,
title:'铲屎官相关信息'
}
}
]
},
{
name:"wangwang",
path:'/dogs',
component:Dogs,
meta:{isDog:true},
//独享守卫:逻辑与全局前置守卫一样
beforeEnter(to,from,next){
if(to.meta.isDog){
if(localStorage.getItem('dogName') == 'dog') {
next()
} else {
alert('暂无权限查看狗狗相关信息')
}
}
}
},
]
})
//全局前置路由守卫 :验证登录信息
router.beforeEach((to,from,next)=>{
if(to.meta.isAuth) {
//铲屎官相关路由组件需要验证name是否为liqi6limi
if(localStorage.getItem('name') == 'liqi6limi') {
next()
} else {
alert('暂无权限查看铲屎官相关信息')
}
}else {
next()
}
})
//全局后置路由守卫:修改网页title
router.afterEach((to,from)=>{
//修改网页标题
if(to.meta.title) {
document.title = to.meta.title
}else {
document.title = '动物学院网站';
console.log(from);
}
})
export default router;
App.vue代码
<template>
<div id="app">
<h3>动物学院</h3>
<button @click="catSchool" class="btn btn-info active" >喵喵院系</button>
<button @click="dogSchool" class="btn btn-info active" >汪汪院系</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
methods: {
catSchool(){
this.$router.push({
name:"miaomiao"
},() => {})
},
dogSchool() {
this.$router.push({
name:"wangwang"
},() => {})
}
}
}
</script>
<style >
button {
margin: 5px;
}
#app {
width: 100%;
text-align: center;
}
li {
line-height: 40px;
height: 40px;
}
span {
margin-right: 5px;
}
</style>
Dogs.vue代码
<template>
<div>
<h3>汪汪学院相关人员</h3>
<p>目前还没有养狗,等我有钱了再养一只修狗</p>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:"Dogs",
}
</script>
Cats.vue代码
<template>
<div>
<h3>喵喵学院相关人员</h3>
<button class="btn btn-success" @click="catAbout">喵喵相关</button>
<button class="btn btn-success" @click="ownerAbout">铲屎官相关</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:"Cats",
methods:{
catAbout(){
this.$router.push({
path:"/cats/catsAbout",
},()=>{})
},
ownerAbout(){
this.$router.push({
name:"zhurenAbout",
},()=>{})
}
}
}
</script>
CatsAbout.vue代码
<template>
<div>
<ul class="list-unstyled">
<li v-for="(cat,index) in cats" :key="index">
<span>喵喵名:{{cat.name}}</span>
<span>性别:{{cat.sex}}</span>
<span>年龄:{{cat.age}}</span>
<span>其他:已绝育,疫苗已打</span>
</li>
</ul>
</div>
</template>
<script>
export default {
name:"CatsAbout",
data(){
return {
cats:[{
name:"憨瓜",
age:"4岁整",
sex:"公"
},{
name:"波妞",
age:"3岁3个月",
sex:"母",
}]
}
},
//进入守卫:通过路由规则进入组件时被调用
beforeRouteEnter(to,from,next){
if(to.meta.isCat){
if(localStorage.getItem('catName') == 'cat') {
next()
} else {
alert('暂无权限查看猫猫相关信息')
}
}
},
//离开守卫:通过路由规则离开时被调用
beforeRouteLeave(to,from,next){
console.log(to,from);
next()
},
}
</script>
OwnerAbout.vue代码
<template>
<div>
<h4>本人养有2只猫:憨瓜和波妞,等我有钱了再养一只狗</h4>
</div>
</template>
<script>
export default ({
name:"OwnerAbout",
})
</script>
运行结果
六、总结
1、独享守卫beforeEnter
只有前置没有后置,可以搭配全局后置守卫afterEach
使用
2、进入守卫beforeRouteEnter
是支持给 next 传递回调的唯一守卫
3、组件内守卫与全局守卫的区别
全局守卫是进入前和进入后,组件内守卫是进入前和出来后,组件守卫也可以搭配全局后置守卫afterEach
使用
4、路由守卫使用场景
① 全局前置守卫beforeEach
通常用来进行路由跳转的一些信息判断,判断是否登录,是否拿到对应的路由权限等等
② 全局后置守卫afterEach
对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
③ 离开守卫beforeRouteLeave
通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消
5、每个守卫方法接收3个参数
to::route, 即将要进入的目标路由;
from: route,当前导航正要离开的路由;
next:function, 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
next 可以返回的值如下:
next()
: 进行下一个路由。如果全部路由执行完了,则导航的状态就是 confirmed (确认的)
next(false)
:取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址
next('/')
或 next({ path: '/' })
:通过一个路由地址跳转到一个不同的地址,就像调用 router.push() 一样,当前的导航被中断,然后进行一个新的导航,就和 from 一样
next(error)
:(2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调
七、路由守卫解析流程
1、导航被触发
2、在失活的组件里调用 离开守卫 beforeRouteLeave
3、调用 全局前置守卫 beforeEach
4、在重用的组件里调用 更新守卫beforeRouteUpdate
(2.2+)(举例来说,对于一个带有动态参数的路径 /user/:id
,在 /users/1
和 /users/2
之间跳转的时候,由于会渲染同样的 UserDetail
组件,因此组件实例会被复用。而这个守卫就会在这个情况下被调用)
5、在路由配置规则里调用 独享守卫 beforeEnter
6、解析异步路由组件
7、在被激活的组件里调用 进入守卫 beforeRouteEnter
8、调用全局解析守卫 beforeResolve
(2.5+)(和 全局前置守卫router.beforeEach
类似,因为它在每次导航时都会触发,但是确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用)
9、导航被确认
10、调用全局后置守卫 afterEach
11、触发 DOM 更新
12、调用 进入守卫 beforeRouteEnter
中传给 next
的回调函数,创建好的组件实例会作为回调函数的参数传入