前言
使用过 Vue 的小伙伴们肯定都知道,Vue 的内部组件keep-alive
是用来缓存我们不活动的组件的。
但是在某些情况下,我们需要缓存,某些情况下希望及时释放掉缓存,那我们应该怎么做呢?
需求
有个场景是,希望模仿App的方式,每次push到当前页面的时候,触发mounted
进行组件初始化,而从其他页面返回到当前页面时,希望可以保留当前组件的状态。
举个例子:
移动端的分页列表页面,加载了几页,并且有滑动记录,但是希望进详情页后,返回列表页时,可以保持分页的状态以及滑动的轨迹。
思考
需求如上,这就需要keep-alive
帮助我们缓存组件了。
但是用过keep-alive
组件的小伙伴肯定明白,如果include
当前列表页面,虽然可以做到返回列表页保持状态,但是从其他页面前往列表页时,依然会加载缓存的状态,我们不得不采用activated
钩子方法来处理,但这样总归是不优雅的。
看过一些文字有人说让include的数组变成动态的是否能达到类似的效果,这种方式也非常好,可以很容易的达到我们想要的效果。
阅读过源码之后发现,keep-alive
内部实现是将组件缓存在一个caches
数组中的,如果我们可以操作这个数组,是否可以达到灵活控制缓存组件的效果呢?
解决方案
-
手动操作
keep-alive
方式其中render方法中部分代码片段
const { cache, keys } = this const key: ?string = vnode.key == null // same constructor may get registered as different local components // so cid alone is not enough (#3269) ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.key if (cache[key]) { vnode.componentInstance = cache[key].componentInstance // make current key freshest remove(keys, key) keys.push(key) } else { // delay setting the cache until update this.vnodeToCache = vnode this.keyToCache = key }
这种情况下,我们可以沿用
key
的定义方式来操作keep-alive
组件的cache
数组,删除已缓存的组件,达到下次进入重新初始化组件的目的。设置路由守卫,监听路由变化时,适时的清理被缓存的组件
定义一个方法,通过
vueInstance.$vnode.parent.componentInstance
获取到keep-alive实例,清楚cache
的同时,destory
当前实例。removeKeepAliveCacheForVueInstance(vueInstance) { let key = vueInstance.$vnode.key ?? vueInstance.$vnode.componentOptions.Ctor.cid + (vueInstance.$vnode.componentOptions.tag ? `::${vueInstance.$vnode.componentOptions.tag}` : ""); let cache = vueInstance.$vnode.parent.componentInstance.cache; let keys = vueInstance.$vnode.parent.componentInstance.keys; if (cache[key]) { vueInstance.$destroy(); delete cache[key]; let index = keys.indexOf(key); if (index > -1) { keys.splice(index, 1); } } }
-
通过动态控制include属性,来动态控制显隐
每次
push
操作时给params带上push
标记-
在路由切换时监听路由状态,如果
to
为push
标记,则add缓存页面,否则,pop缓存页面watch: { $route(to) { // 如果涉及到同一页面的push操作,需要去重后插入 if (to.params.routeType == "push") { this.include.push(to.name); } else { this.include.pop(); } // 处理tabbar需要清栈的情况 if (to.params.clearRoute) { this.include = ["Index"]; } }, },
小结
方法一比较繁琐,但是用起来还是很直接的,比较灵活,可以应付浏览器刷新的场景
方法二比较简单,适合在移动app中使用,自己维护页面栈,不会有浏览器刷新等操作,否则include的内容可能会由于刷新而出现栈异常