概念
<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
接收三个值
- include 只有名称匹配的组件才能被缓存
- exclude 名称匹配的组件不能被缓存
- max 最大缓存实例数
<!-- 以英文逗号分隔的字符串 -->
<KeepAlive include="a,b">
<component :is="view" />
</KeepAlive>
<!-- 正则表达式 (需使用 `v-bind`) -->
<KeepAlive :include="/a|b/">
<component :is="view" />
</KeepAlive>
<!-- 数组 (需使用 `v-bind`) -->
<KeepAlive :include="['a', 'b']">
<component :is="view" />
</KeepAlive>
作用
主要用于保留组件状态或避免重新渲染。
原理
keep-alive运用了 LRU 算法,来实现组件缓存,其原理如下
-
获取
keep-alive标签里的第一个子元素 -
如果子元素的name不在
inclue或者在exluede,则直接返回vnode,否则进行缓存 -
根据组件的id和tag生成key,如果缓存对象中有对应key的实例,则通过LRU算法实现实例的位置置换
-
若缓存对象中没有对应key的实例,则会判断是否超出
max值,超出则删除最久未使用的实例
LRU 算法
LRU是 Least Recently Used 的缩写,即最近最少使用。
当缓存使用的空间达到上限后,就需要从已有的数据中淘汰一部分以维持缓存的可用性,而淘汰数据的选择就是通过LRU算法完成的。
它的核心思想是当缓存满时,会优先淘汰那些最近最少使用的缓存对象。
借助其他大佬画的图,加深理解一下,示例设置缓存大小为3,依次访问页面
通过上图的理解,可以得出LRU算法的关键的四个点
- 有限的存储空间
- 顺序的存储结构(前端类似的有Map/Array)
- 最近使用的元素置于栈顶
- 栈满则删除栈底元素(栈底元素就代表最近最少使用)
源码解析
展示keep-alive的主要代码
render () {
const slot = this.$slots.default
const vnode = getFirstComponentChild(slot)
// 判断vnode是否是组件
const componentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
const name = getComponentName(componentOptions)
const { include, exclude } = this
// 如果name不在inlcude中或者存在于exlude中则表示不缓存,直接返回vnode
if (
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, name))
) {
return vnode
}
const { cache, keys } = this
// 根据组件ID和tag生成缓存Key
const key: = vnode.key == null
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.key
// 如果缓存对象中存在该key对象,则将其置顶
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
remove(keys, key)
keys.push(key)
} else {
cache[key] = vnode;
keys.push(key);
// 检查缓存的实例数量是否超过max设置值,超过则根据LRU置换策略删除最近最久未使用的实例
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode);
}
}
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}