如果你用了官方组件keep-alive,那么如何在合适的时机销毁掉缓存(设置最大缓存数无疑是一个简单却不怎么有效的方案),如何在缓存中创建两个相同组件的缓存实例,将会是两个不可避免的问题。
问题分析:
Vue.js中有官方插件Vue-router帮助我们实现SPA,同时为了提高页面二次响应速度,Vue.js提供了官方的keep-alive组件帮助我们缓存页面,但是有很多场景keep-alive配合Vue-router并不能完美胜任,需要极为复杂的特殊处理,举两个常见业务场景(前提:页面都使用keep-alive):
- 页面A 跳转 页面A时 (如:商品详情页跳转商品详情页) (此时需要创建两个页面A实例)
- 页面A 跳转 页面B,再返回页面A时 (此时销毁页面B)
场景一常见的问题有(可略过不看):
- 如果不做处理,
keep-alive会把页面A缓存,再次进入页面A时不会创建新的Vue实例,而是创建完Vnode后直接把之前缓存的Vue实例拿过来用,所以此时路由虽然有变化,但是并不会走生命周期中的created等方法。此时需要单独处理,监听$route,然后取出query中的字段,重新请求数据。 - 此时如果返回,实际上还是会走到
$route的监听方法重新请求数据,也就是说页面虽然缓存了,但是之前的数据没有缓存,此时常见的做法就是维护一个Map,保存对应query的请求数据,这无疑又增加了代码的复杂度。 - 我们经常需要记录页面的滚动位置,在返回到当前页面是滚动到对应位置,此时无疑也需要单独针对
$route.query来进行保存滚动位置,又增加了复杂度。
场景二的问题 (可略过不看):
页面A跳转页面B,返回后再次跳转页面B时,如果没有对页面B处理,此时页面B的生命周期不会执行(而会走activited),因为Vue实例已经创建,直接从之前的cache中取出来,不需要重新创建。解决这个问题的方法有很多种,可以在<router-view/>中增加动态key的绑定,每次跳转页面B,用当前时间来修改绑定的Key,这样再次进入页面B就会判定为新的实例,重新创建。- 如果使用上面的方式解决了问题,会导致新的问题,缓存的页面B如何销毁。此时不能在
beforeRouterLeave中销毁,因为A->B->C时,我们需要缓存B,如果你的页面有自定义的返回键此时需要销毁当前实例,并且需要把缓存中的cache map和keys更新,但是如果有物理返回键或者浏览器返回键时,此方法失效。所以我们需要一个精确的返回钩子来销毁页面
如何托管
如果你碰到了上面说的任意一个问题,那么你面临的就是和我相同的问题。我们期望的结果是 A->A->A 时,会创建三个Vue实例,返回时能够自动销毁当前实例。其次是这个方案对现有的项目不能够有侵入,无需修改代码。
好了不装了,摊牌了:
在项目中引入这个库vue-router-keep-alive-helper
npm i -s vue-router-keep-alive-helper
在创建VueRouter后使用 createHelper
import createHelper from 'vue-router-keep-alive-helper' //增加
import Vue from 'vue'
const router = new VueRouter({routes})
createHelper({Vue, router}); //增加
其他的事就完全不用关心了。
来看引入该库的案例,github地址
Bilibili演示视频
PS: 注意Vue devtool 有点小BUG,返回时虽然销毁了实例和缓存,但是需要刷新一下devtool才能够看到更新,但其实已经销毁了,可以查看console中的日志。
欢迎留言讨论