全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 10 分钟 ✍️ juanwangdev

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包含完整组件实例引用,恢复时直接复用

📝 发现内容有误?点击此处直接编辑

← 上一篇 虚拟DOM与VNode
下一篇 → 事件系统与指令处理底层实现
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库