前言

写了几个项目,中间使用不同的keep-alive用法,发现这东西并没有想象中的那样简单,一不小心就会踩坑。

KeepAlive

<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
includeexclude prop 允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示。匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。

当我们使用<keep-alive>组件时的期望:
第一次进入页面:beforeCreate => created => beforeMount => mounted => activated
跳出页面:deactivated
第二次进入页面:activated

方案一

网上很多keepAlive方案都是如下这种

// App.vue
<keep-alive v-if="$route.meta.keepAlive">
<router-view></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

// router
{
path: '/',
name: 'Home',
component: Home,
meta: {
keepAlive: true,
}
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue'),
}

以上代码我们将Home页做了keepAlive,打印Home页的生命周期为:
第一次进入Home页:beforeCreate => created => beforeMount => mounted => activated
跳出Home页:deactivated => beforeDestroy => destroyed
第二次进入Home页:beforeCreate => created => beforeMount => mounted => activated

???缓存了个寂寞???

我们将About页面也加上keepAlive

// router
{
path: '/',
name: 'Home',
component: Home,
meta: {
keepAlive: true,
}
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue'),
meta: {
keepAlive: true,
}
}

重复上次操作,打印Home页的生命周期为:
第一次进入Home页:beforeCreate => created => beforeMount => mounted => activated
跳出Home页:deactivated
第二次进入Home页:activated

发现结果是我们想要的了,但是这样并不能满足实际使用情况,所以我们先还原

// router
{
path: '/',
name: 'Home',
component: Home,
meta: {
keepAlive: true,
}
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue'),
}

回顾代码发现其实是v-if销毁了router-view组件,所以此种方案PASS

方案二

受上述案例提醒,router-view只能使用一个,那么只能从keep-alive找方案了

<!-- App.vue -->
<keep-alive :include="keepAliveList">
<router-view></router-view>
</keep-alive>
// 获取所有需要keepAlive的页面name,name需要跟component name相同
computed: {
keepAliveList() {
const keeps = routes.filter(page => (page.meta && page.meta.keepAlive))
return keeps.map(page => page.name)
}
}

执行以上代码:
第一次进入Home页:beforeCreate => created => beforeMount => mounted => activated
跳出Home页:deactivated
第二次进入Home页:activated

注意

  1. <keep-alive> 不会在函数式组件中正常工作,因为它们没有缓存实例。

其他问题方案

做APP的时候我们一般都会有这样的场景:

  1. 首页 => 列表 => 详情 | 详情 => 列表 列表 页面需要缓存
  2. 列表 => 首页 列表 页面需要主动清除缓存

解决方案一: this.$destroy()
解决方案二: 动态控制include

注意: 方案一有BUG,推荐使用方案二