文章目录
简介:正如其名,vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。导航守卫就是路由跳转过程中的一些钩子函数,再直白点路由跳转是一个大的过程,这个大的过程分为跳转前中后等等细小的过程,在每一个过程中都有一函数,这个函数能让你操作一些其他的事儿的时机,这就是导航守卫
一、导航守卫全解析
钩子也有回调的意思hook,后置钩子没有next
先看一个钩子函数执行后输出的顺序截图
导航守卫分类
- 全局
- 单个路由独享
- 组件内
全局导航守卫
【全局的】:是指路由实例上直接操作的钩子函数
,他的特点是所有路由配置的组件都会触发
,直白点就是触发路由就会触发这些钩子函数,如下的写法。钩子函数按执行顺序包括beforeEach
、beforeResolve
(2.5+)、afterEach
三个(以下的钩子函数都是按执行顺序讲解的):
全局导航守卫使用:
1.在index.js创建路由实例
2.router掉用beforeEach
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// from来自哪里
// to去哪里
// next 必须调用的
})
[beforeEach]:在路由跳转前触发,参数包括to,from,next(参数会单独介绍)三个,这个钩子作用主要是用于登录验证,也就是路由还没跳转提前告知,以免跳转了再通知就为时已晚。
[beforeResolve](2.5+):这个钩子和beforeEach类似,也是路由跳转前触发,参数也是to,from,next三个,和beforeEach区别官方解释为区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
即在 beforeEach 和 组件内beforeRouteEnter 之后,afterEach之前调用。
[afterEach]:和beforeEach相反,他是在路由跳转完成后触发,参数包括to,from没有了next(参数会单独介绍),他发生在beforeEach和beforeResolve之后,beforeRouteEnter(组件内守卫,后讲)之前。
独享守卫
【路由独享的】 是指在单个路由配置的时候也可以设置的钩子函数,其位置就是下面示例中的位置,也就是像Foo这样的组件都存在这样的钩子函数。目前他只有一个钩子函数beforeEnter
:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
[beforeEnter]:和beforeEach完全相同,如果都设置则在beforeEach之后紧随执行,参数to、from、next
组件内守卫
【组件内的】:是指在组件内执行的钩子函数,类似于组件内的生命周期,相当于为配置路由的组件添加的生命周期钩子函数。钩子函数按执行顺序包括beforeRouteEnter
、beforeRouteUpdate
(2.2+)、beforeRouteLeave
三个,执行位置如下:
<template>
...
</template>
export default{
data(){
//...
},
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
<style>
...
</style>
[beforeRouteEnter]:路由进入之前调用,参数包括to,from,next。该钩子在全局守卫beforeEach和独享守卫beforeEnter之后,全局beforeResolve和全局afterEach之前调用,要注意的是该守卫内访问不到组件的实例,也就是this为undefined,也就是他在beforeCreate生命周期前触发。在这个钩子函数中,可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数,可以在这个守卫中请求服务端获取数据,当成功获取并能进入路由时,调用next并在回调中通过 vm访问组件实例进行赋值等操作,(next中函数的调用在mounted之后:为了确保能对组件实例的完整访问)。
beforeRouteEnter (to, from, next) {
// 这里还无法访问到组件实例,this === undefined
next( vm => {
// 通过 `vm` 访问组件实例
})
}
[beforeRouteUpdate] (v 2.2+):在当前路由改变时,并且该组件被复用时调用,可以通过this访问实例。参数包括to,from,next。可能有的同学会疑问,what is 路由改变 or what is 组件被复用?
- 对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,组件实例会被复用,该守卫会被调用
- 当前路由query变更时,该守卫会被调用
[beforeRouteLeave]:导航离开该组件的对应路由时调用,可以访问组件实例this,参数包括to,from,next。
至此,所有钩子函数介绍完毕
导航守卫总结
全局路由钩子: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)
不知道你是否还记得to、from、next这三个参数
下面请重头把这几个钩子函数的参数看一遍,细心的同学可以看见在afterEach钩子中参数没有next,为什么呢?这就要看一下回调参数了
回调参数
to:目标路由对象;
from:即将要离开的路由对象;
next:他是最重要的一个参数,他相当于佛珠的线,把一个一个珠子逐个串起来。以下注意点务必牢记:
1.但凡涉及到有next参数的钩子,必须调用next() 才能继续往下执行下一个钩子,否则路由跳转等会停止。
2.如果要中断当前的导航要调用next(false)。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from路由对应的地址。(主要用于登录验证不通过的处理)
3.当然next可以这样使用,next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。意思是当前的导航被中断,然后进行一个新的导航。可传递的参数与router.push中选项一致。
4.在beforeRouteEnter钩子中next((vm)=>{})内接收的回调函数参数为当前组件的实例vm,这个回调函数在生命周期mounted之后调用,也就是,他是所有导航守卫和生命周期函数最后执行的那个钩子。
5.next(error): (v2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
我们再看一遍开头的图片
我们最后屡一下顺序:
当点击切换路由时:beforeRouterLeave–>beforeEach–>beforeEnter–>beforeRouteEnter–>beforeResolve–>afterEach–>beforeCreate–>created–>beforeMount–>mounted–>beforeRouteEnter的next的回调
当路由更新时:beforeRouteUpdate
二、keepalive
简介:router-view 也是一个组件,如果被包裹在keepalive里面,所有路径匹配到的视图组件都会被缓存,可以避免重现渲染。项目主流程缓存优化,主流程页面(组件)切换时保持之前加载的状态,避免反复渲染影响页面性能,同时也可以很大程度上减少接口请求,减小服务器压力
activated活跃 与 deactivated不活跃
被keep-alive包含的组件/路由中,会多出两个生命周期的钩子
:activated 与 deactivated
,这两个函数,只有该组件被保持状态使用keepalive时才会生效
activated在组件第一次渲染时会被调用,之后在每次缓存组件被激活时调用。
activated调用时机:
第一次进入缓存路由/组件,在mounted后面,beforeRouteEnter守卫传给 next 的回调函数之前调用:
beforeMount=> 如果你是从别的路由/组件进来(组件销毁destroyed/或离开缓存deactivated)=>mounted=> activated 进入缓存组件 => 执行 beforeRouteEnter回调
因为组件被缓存了,再次进入缓存路由/组件时,不会触发这些钩子:// beforeCreate created beforeMount mounted 都不会触发。
deactivated:组件被停用(离开路由)时调用
使用了keep-alive就不会调用beforeDestroy(组件销毁前钩子)和destroyed(组件销毁),因为组件没被销毁,被缓存起来了。
这个钩子可以看作beforeDestroy的替代,如果你缓存了组件,要在组件销毁的的时候做一些事情,你可以放在这个钩子里。
如果你离开了路由,会依次触发:
组件内的离开当前路由钩子beforeRouteLeave => 路由前置守卫 beforeEach =>全局后置钩子afterEach => deactivated 离开缓存组件 => activated 进入缓存组件(如果你进入的也是缓存路由
如何使用
方式一、利用meta标签
直接上代码,
1、首先在路由中的meta标签中记录keepAlive的属性为true
path: '/classify',
name: 'classify',
component: () => import('@/views/classify/classify.vue'),
meta: {
title: '雷石淘券券',
keepAlive: true
}
},
2、在创建router实例的时候加上scrollBehavior方法
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes,
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return {
x: 0,
y: 0
}
}
}
})
3、在需要缓存的router-view组件上包裹keep-alive组件
<keep-alive>
<router-view v-if='$route.meta.keepAlive'></router-view>
</keep-alive><router-view v-if='!$route.meta.keepAlive'></router-view>
4、由于有些情况下需要缓存有些情况下不需要缓存,可以在缓存的组件里的路由钩子函数中做判断
beforeRouteLeave (to, from, next) {
this.loading = true
if (to.path === '/goods_detail') {
from.meta.keepAlive = true
} else {
from.meta.keepAlive = false
// this.$destroy()
}
next()
},
支持可以支持组件的缓存,但是这种方法有bug,首先第一次打开页面的时候并不缓存,即第一次从列表页跳到详情页,再回来并没有缓存,后面在进入详情页才会被缓存
并且只会缓存第一次进入的状态,不会重新请求数据,如果当页面A选中一个分类跳到B页面,再从B列表页面跳往详情页,此时会缓存这个状态,并且以后再从A页面的其他分类跳到B页面都不会重新被缓存,以至于每次从详情页返回B页面都会跳第一次缓存的状态;当你的项目只有一种状态需要缓存,可以考虑使用这种方法
方式二、使用include、exclude属性(推荐)
include和exclude
include是需要缓存的组件;
exclude是除了某些组件都缓存;
include 和 exclude 属性允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示:
<!-- 逗号分隔字符串 -->
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。
max只在2.5.0新增,最多可以缓存多少组件实例。一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。
<keep-alive :max="10">
<component :is="view"></component>
</keep-alive>
keepalive总结
项目中缓存使用方法:
1、在创建的router对象上加scrollBehavior方法,同上;
2、将需要缓存的组件加在include属性里
<keep-alive :include="['home','classify','search']">
<router-view></router-view>
</keep-alive>
—end