自建博客文章链接:http://www.heblogs.cn/articleDetails/60fa2935b69f2b0a1af64791
场景
- 从A页面进入B页面 ,刷新页面数据(keepAlive: ture)
- 离开B页面进入C页面,缓存B页面数据(keepAlive: true)
- 从C页面进入B页面,读取缓存数据 返回滚动高度(keepAlive: true)
- 离开B页面进入A页面,不缓存B页面数据(keepAlive: false)
概念
- keep-alive
- keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
- keep-alive: vue文档
- 组件内的守卫 - beforeRouteLeave
- 导航离开该组件的对应路由时调用
- 可以访问组件实例
this
- 组件内的守卫: vue-router 文档
前置代码
- 路由元信息内添加特定字段如:keepAlive
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
meta: {
keepAlive: true
}
}
]
})
- 父组件内根据路由中的keepAlive字段动态使用keep-alive标签
<template>
<div id="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
</template>
思路
由于现在组件的keep-alive是动态根据路由元信息中的keepAlive字段进行动态使用的,所以只要动态改变对应路由元信息的keepAlive字段就可以实现动态缓存。
实现方案
方案一
- 利用beforeRouteLeave改变from的keepAlive实现(原思路,网络解决方案之一,有bug)
beforeRouteLeave (to, from, next) {
// 判断是否是去往页面 C
if (to.name === 'C') {
// 去 C 页面,缓存
from.meta.keepAlive = true
} else {
// 不是去 C 页面,不缓存
from.meta.keepAlive = false
}
next()
}
bug:从A进入到B 正常刷新 进入到C 再返回B页面 缓存成功 再返回到A 进入到B 正常刷新 进入到C返回B 缓存为上次缓存的内容
方案二
- $destroy()销毁
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 判断是否是去往页面 C
if (to.name !== 'C') {
// 不是去 C 页面,不缓存
this.$destroy()
}
next()
}
bug:销毁之后永远不会被缓存
方案三
不从开关keepalive出发 不去关闭keepalive 而是跳转带参执行activated内方法 从A页面进入
A页面:this.$router.push({
path:'B'},query:{
from:'A'})
B页面:
activated () {
const keepAliveFrom = this.$router.query.from
if (keepAliveFrom === 'A') {
...
// 执行mounted
}else{
...
// 执行activated
}
},
bug:从C物理返回B后 参数不会改变
方案四
- 不操作beforeRouteLeave中的from对象改变keepAlive
- 直接操作this.$router中对应路由元信息的keepAlive
// 封装操作指定name的路由的元信息
private changeKeepAlive (parentName: string, name: string, keepAlive: boolean) {
// @ts-ignore
this.$router.options.routes.map((item: any) => {
if (item.name === parentName) {
item.meta.keepAlive = keepAlive
}
})
}
bug:类似方案一 只是修改了操作keepalive的方式 由直接才做路由元信息去改变
方案五
改造方案三
- 不开关keepalive 一直打开 根据页面来去方向 在activated执行方法
- 从A C页面进入B页面 使用缓存页面记录 在B页面判断方向 调用activated内的方法
但是此方案缺点比较明显 若keepalive 全程保持打开 数据并不能初始化 需要一个一个去初始化
A页面:this.$router.push({
path:'B'})
localStroge.setItem('keepAliveFrom','A')
B页面:
beforeRouteLeave (to, from, next) {
localStorage.removeItem('keepAliveFrom') // 当离开页面 清除keepAliveFrom 下次进入走activated
next()
},
activated () {
const keepAliveFrom = localStroge.getItem('keepAliveFrom')
if (keepAliveFrom === 'my') {
...
// 执行mounted
}else{
...
// 执行activated
}
},
总结:以上方案总会出点大大小小的bug 具体实现效果与预知效果差距太远,差距原因主要在于在APP.vue文件内有两个router-view 由keepalive控制开关 所以会导致keepalive不能开了就更新
方案六
在路由表路由信息给需要缓存的路由加上name name名字要与组件内名字一致
const router = new VueRouter({
routes: [
{
path: '/A', name:'A', component: A },
{
path: '/B', name:'B', component: B },
{
path: '/C', name:'C', component: C }
]
})
父组件内根据路由中的router-view标签用keep-alive包裹起来 加上属性include
<template>
<div id="app">
<keep-alive :include="keepAlive">
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
}
},
computed: {
keepAlive () {
return this.$store.state.keepAlive
}
}
}
</script>