最近在做优化的时候,为了避免组件多余的重复渲染,用vue提供的keep-alive缓存组件,基本用法很简单
app.vue中
<keep-alive>
<router-view v-if="$route.meta.keepAlive">
<!-- 这里是会被缓存的视图组件 -->
</router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive">
<!-- 这里是不被缓存的视图组件 -->
router.js
需要缓存的组件meta对象加keepAlive:true
{
path: "/ProjectDetail",
name:'ProjectDetail',
component: ProjectDetail,
meta:{
keepAlive:true,
}
到这里,看似目的达到了,缓存的页面不会重新渲染,但是有一个问题,组件缓存了之后,任何时候都不会重新加载,而实际的需求却不是这么简单粗暴,举个例子,A-B-C三个层层递进的页面,A是首页,B是搜索页面,C是通过搜索列表进入的详情页面,从A进入B再进入C,返回B页面,想要展示 B页面之前的状态,但是从A再进入B页面,不想进入B的缓存页面,经过琢磨,想到了组件内的路由钩子应该能解决这个问题,于是进行尝试
方案一
A页面写了
beforeRouteLeave(to, from, next) {
to.meta.keepAlive = false;
next();
}
C页面写了
beforeRouteLeave(to, from, next) {
to.meta.keepAlive = true;
next();
}
这样改完之后试了一下,果然从 B到C,再返回B用了缓存页面,再回到A去B,重新创建了B的实例,但是再次操作,发现B组件再怎么设置keep-alive都无法缓存了
今天看了下keep-alive源码,证实了meta.keepAlive=true这种方法,解决不了条件缓存问题
原因:在keep-alive源码中,include和exclude是被watch的,当发生变化时,keep-alive会去校验cache里是否匹配,匹配不上的会被删除。也就是说,官方是为这种情况做了处理的。而meta这种方法,因为没有存在某种类似于watch的方法,导致这种方法天然是和实际cache里面的内容有出入的,所以可定会存在各种奇怪的bug。
经过各种查资料,又尝试了方案二
方案二
利用include,动态添加"B"
1.app.vue
<keep-alive :include="catchList">
<router-view></router-view>
</keep-alive>
这里的catchList,是vuex维护的需要缓存的组件名的一个数组
2、在路由中加入
router.beforeEach((to, from, next) => {
if (to.name === 'B') {
store.commit('keepAlive', 'B')
}
next()
})
3、在b.vue中加入
beforeRouteLeave (to, from, next) {
if (to.name !== 'C') {
this.$store.commit('noKeepAlive')
}
next()
}
4.在vuex中mutation
keepAlive(state, component) {
!state.catchList.includes(component) &&
state.catchList.push(component)
},
noKeepAlive(state) {
state.catchList = []
}
大致的原理是,只要是B的组件,都缓存。只有当从B>A的时候,才让B不缓存。
亲测同原理,但是meta.keepAlive会出现其他bug
所以,目前来看,能用的方法,其实只有这一种。