1. 1. 需求
  2. 2. keep-alive
  3. 3. 需求实现
    1. 3.1. 如何判断页面的前进后退
    2. 3.2. 如何触发缓存页面数据的更新
Vue之keep-alive使用指南

需求

在做业务的时候经常会碰到这样的互动需求:

  • 用户前进时,总是进入一个新的页面(不使用缓存)
  • 用户后退时,需要保留之前的操作(使用缓存)

vue-router中,切换路由时并不会保存组件的状态,而是会重新创建新组件,走一遍完整的生命周期,我们要实现上面的需求,就需要使用keep-alive组件来解决。

keep-alive

通过结合router-view我们就可以实现缓存页面的问题:

1
2
3
4
<keep-alive>
<router-view v-if="isKeepAlive" :key="keepAliveKey" transition transition-mode="out-in"></router-view>
</keep-alive>
<router-view v-if="!isKeepAlive" transition transition-mode="out-in"></router-view>

isKeepAlive的判断可以通过 route.meta`来配置哪些路由需要缓存,而`keepAliveKey`可以简单的使用` route.fullPath,根据使用场景而定。

因为使用了keep-alive,不论是前进还是后退,我们总会进入到缓存的组件,那么怎么才能进入新页面呢?首先可以想到的是使用$destroy删除组件,但是实际操作的时候发现缓存依然存在。于是通过万能的搜索找到了一个解决方案:Vue 全站缓存之 keep-alive : 动态移除缓存

简而言之就是需要自己找到keep-alive的实例手动删除,并销毁组件。

1
2
3
4
5
6
7
8
9
10
11
12
const destroy = (key) => {
// 找到keepAlive组件
const keepAliveVm = global.keepAliveVm // 具体怎么找到keeyAlive组件自己看情况
const cache = keepAliveVm.cache
cosnt keys = keepAliveVm.keys
// 删除keys
if (keys.indexOf(key) > -1) keys.splice(keys.indexOf(key), 1)
// 删除cache
delete cache[key]
// $destroy组件
vnodes[key].$destroy()
}

注意点: 在使用max时同样会存在缓存不删除的情况,需要自己手动删除。

需求实现

如何判断页面的前进后退

我们可以在配置路由时,给每个页面设置层级关系:

1
2
3
4
routes: [
{ path: '/list', component: List, meta: { level: 1 } },
{ path: '/edit', component: Edit, meta: { level: 2 } }
]

然后在Vue全局注册一个混入方法,在每次路由切换的时候判断层级变化,控制是否要删除缓存:

1
2
3
4
5
6
7
8
9
10
11
12
13
Vue.mixin({
befroeRouteLeave (to, from, next) {
if (from && from.meta.level && to.meta.level && from.meta.level > to.meta.level) {
// 如果是后退,则删除组件缓存
const keepAliveVm = this.$vnode.parent.componentInstance
const { keys, cache } = keepAliveVm
const key = this.$route.fullPath
if (keys.indexOf(key) > -1) keys.splice(keys.indexOf(key), 1)
delete cache[key]
this.$destroy()
}
}
})

如何触发缓存页面数据的更新

如果使用了keep-alive,再重新渲染这个组件的时候不会再触发生命周期的createdmounted,而是触发activated。所以我们可以在activated中去执行我们想要的数据更新:

1
2
3
4
5
6
7
8
Vue.mixin({
created () {
this.pageEnter && this.pageEnter()
},
activated () {
this.pageEnter && this.pageEnter()
}
})