keep-alive遇到的坑

最近在做优化的时候,为了避免组件多余的重复渲染,用vue提供的keep-alive缓存组件,基本用法很简单

app.vue中



     
          
      
  


   

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


   

这里的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
所以,目前来看,能用的方法,其实只有这一种。

你可能感兴趣的:(Vue)