KeepAlive与缓存机制底层实现
KeepAlive是抽象组件,通过 cache 对象缓存VNode,LRU策略管理缓存池,实现组件实例复用。
KeepAlive抽象组件
JavaScript
export default {
name: 'keep-alive',
abstract: true, // 不渲染真实DOM
props: {
include: [String, RegExp, Array], // 缓存匹配
exclude: [String, RegExp, Array], // 不缓存匹配
max: [String, Number] // 最大缓存数
},
created() {
this.cache = Object.create(null) // 缓存池 {key: VNode}
this.keys = [] // 缓存key列表(LRU顺序)
},
destroyed() {
for (const key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys)
}
}
}
abstract: true 使KeepAlive不产生真实DOM节点,只管理缓存。
渲染与缓存逻辑
JavaScript
render() {
const slot = this.$slots.default
const vnode = getFirstComponentChild(slot)
const componentOptions = vnode?.componentOptions
if (!componentOptions) return vnode
const name = getComponentName(componentOptions)
const { include, exclude } = this
// 匹配检查
if (name && !matches(name, include, exclude)) {
return vnode // 不匹配,不缓存
}
const key = vnode.key == null
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
const { cache, keys } = this
if (cache[key]) {
// 缓存命中,复用实例
vnode.componentInstance = cache[key].componentInstance
// LRU: 移到末尾表示最近使用
remove(keys, key)
keys.push(key)
} else {
// 缓存未命中,存入缓存
cache[key] = vnode
keys.push(key)
// 超出max限制,删除最久未使用
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}
vnode.data.keepAlive = true
return vnode
}
缓存命中复用实例,未命中存入缓存,LRU策略管理缓存顺序。
LRU缓存管理
JavaScript
// 缓存命中时
remove(keys, key) // 从数组移除
keys.push(key) // 添加到末尾
// 超出限制时
function pruneCacheEntry(cache, key, keys, current) {
const cached = cache[key]
if (cached && (!current || cached.tag !== current.tag)) {
cached.componentInstance.$destroy() // 销毁组件实例
}
cache[key] = null
remove(keys, key)
}
LRU通过数组顺序管理,最近使用的移到末尾,超限时删除头部(最久未使用)。
组件激活与停用
JavaScript
// KeepAlive包裹的组件会触发特殊钩子
{
activated() {
// 从缓存恢复时调用
// 组件实例已存在,不重新创建
},
deactivated() {
// 被移出视图时调用
// 组件实例保留在缓存中
}
}
// 钩子触发时机
// 首次进入: created -> mounted -> activated
// 再次进入: activated (跳过created/mounted)
// 离开时: deactivated (不触发destroyed)
通过 activated/deactivated 区分首次创建和从缓存恢复。
include/exclude匹配
JavaScript
function matches(pattern, include, exclude) {
if (include) {
if (typeof include === 'string') {
return pattern.indexOf(include) > -1
}
if (Array.isArray(include)) {
return include.some(p => checkMatch(p, pattern))
}
if (isRegExp(include)) {
return include.test(pattern)
}
}
if (exclude) {
if (typeof exclude === 'string') {
return pattern.indexOf(exclude) === -1
}
if (Array.isArray(exclude)) {
return exclude.every(p => !checkMatch(p, pattern))
}
if (isRegExp(exclude)) {
return !exclude.test(pattern)
}
}
return true
}
支持字符串包含、数组匹配、正则三种方式,include和exclude可组合使用。
缓存VNode结构
JavaScript
// 缓存的VNode包含完整组件信息
{
tag: 'vue-component-1-MyComponent',
componentInstance: ComponentInstance, // 组件实例引用
componentOptions: {
Ctor: VueComponent, // 组件构造函数
propsData: {...}, // 属性数据
listeners: {...}, // 事件监听
tag: 'MyComponent'
},
data: {
keepAlive: true // 标记为KeepAlive缓存
}
}
缓存的VNode包含组件实例引用,恢复时直接复用不重新创建。
要点总结
- KeepAlive是抽象组件(
abstract: true),不渲染真实DOM - 通过
cache对象和keys数组实现LRU缓存策略 - 缓存命中复用实例,LRU移到末尾;未命中存入缓存
- 超出
max限制时删除最久未使用的缓存项 activated/deactivated钩子区分首次创建和缓存恢复include/exclude支持字符串、数组、正则三种匹配- 缓存VNode包含完整组件实例引用,恢复时直接复用
📝 发现内容有误?点击此处直接编辑