Vue3 的 KeepAlive 组件 include/exclude 匹配算法与缓存淘汰机制
字数 1429 2025-12-15 09:18:28

Vue3 的 KeepAlive 组件 include/exclude 匹配算法与缓存淘汰机制

描述
Vue3 的 <KeepAlive> 组件可以缓存动态组件的实例,避免重复渲染。其 includeexclude 属性用于控制哪些组件需要缓存或排除缓存,内部基于组件的 name 选项进行匹配。同时,缓存系统采用 LRU(最近最少使用)策略管理缓存容量,确保内存不会无限增长。这里重点讲解匹配算法的实现细节与缓存淘汰机制的原理。

解题过程循序渐进讲解

步骤 1:缓存数据结构设计
KeepAlive 内部维护一个缓存对象(Map)和一个键的数组(数组模拟 LRU 队列)。

// 简化表示
const cache = new Map() // 键 -> 组件实例
const keys = [] // 键的顺序数组,尾部为最近使用
  • 键(key):由组件的虚拟节点(vnode)的 typekey 属性组合生成,确保唯一性。
  • 值(value):组件的实例(vnode.component)。
  • keys 数组记录键的访问顺序,用于实现 LRU 淘汰。

步骤 2:include/exclude 匹配算法
匹配发生在组件被激活(activated)或卸载(deactivated)时。

  1. 获取组件的 name:从组件选项(vnode.type.name)或组件实例的 name 属性获取。
  2. 标准化 includeexclude:它们可以是字符串、正则表达式或数组,编译时会被统一为数组形式。
  3. 匹配逻辑:
    • 如果 include 存在,检查 name 是否在 include 列表中 → 不在则直接返回,不缓存。
    • 如果 exclude 存在,检查 name 是否在 exclude 列表中 → 在则直接返回,不缓存。
  4. 匹配方法:内部使用 matches 函数,支持字符串(如 "ComponentA")和正则表达式(如 /^Async/)的匹配。

步骤 3:缓存淘汰机制(LRU 实现)
KeepAlive 通过 max 属性设置最大缓存数,默认无限制。

  1. 当缓存新实例时,将键添加到 keys 尾部。
  2. 访问已缓存实例时,将对应键移动到 keys 尾部(标记为最近使用)。
  3. 当缓存数量超过 max 时,触发淘汰:
    • 移除 keys 头部的键(最久未使用)。
    • cache 中删除对应实例,并调用该实例的 unmounted 钩子进行清理。

步骤 4:生命周期与缓存状态同步
缓存的组件实例不会触发普通的 unmounted,而是触发 deactivated 钩子(失活)。激活时触发 activated 钩子。

  • 缓存实例时:从原容器移动到隐藏的容器(storageContainerdiv 元素),DOM 被隐藏而非销毁。
  • 激活实例时:从隐藏容器移回实际容器。

步骤 5:键的生成与更新
键基于组件的 typekey 生成。如果用户为动态组件绑定 key,缓存会以 key 为区分(例如同一组件不同数据视为不同缓存)。无 key 时则用组件类型标识,可能导致相同类型组件被覆盖缓存。

总结
KeepAlive 通过 include/excludename 匹配算法筛选缓存目标,通过 LRU 队列(keys 数组 + max 限制)管理缓存容量,结合 DOM 移动而非销毁的机制实现组件实例的高效复用。

Vue3 的 KeepAlive 组件 include/exclude 匹配算法与缓存淘汰机制 描述 : Vue3 的 <KeepAlive> 组件可以缓存动态组件的实例,避免重复渲染。其 include 和 exclude 属性用于控制哪些组件需要缓存或排除缓存,内部基于组件的 name 选项进行匹配。同时,缓存系统采用 LRU(最近最少使用)策略管理缓存容量,确保内存不会无限增长。这里重点讲解匹配算法的实现细节与缓存淘汰机制的原理。 解题过程循序渐进讲解 : 步骤 1:缓存数据结构设计 KeepAlive 内部维护一个缓存对象(Map)和一个键的数组(数组模拟 LRU 队列)。 键(key):由组件的虚拟节点(vnode)的 type 和 key 属性组合生成,确保唯一性。 值(value):组件的实例(vnode.component)。 keys 数组记录键的访问顺序,用于实现 LRU 淘汰。 步骤 2:include/exclude 匹配算法 匹配发生在组件被激活(activated)或卸载(deactivated)时。 获取组件的 name :从组件选项( vnode.type.name )或组件实例的 name 属性获取。 标准化 include 和 exclude :它们可以是字符串、正则表达式或数组,编译时会被统一为数组形式。 匹配逻辑: 如果 include 存在,检查 name 是否在 include 列表中 → 不在则直接返回,不缓存。 如果 exclude 存在,检查 name 是否在 exclude 列表中 → 在则直接返回,不缓存。 匹配方法:内部使用 matches 函数,支持字符串(如 "ComponentA" )和正则表达式(如 /^Async/ )的匹配。 步骤 3:缓存淘汰机制(LRU 实现) KeepAlive 通过 max 属性设置最大缓存数,默认无限制。 当缓存新实例时,将键添加到 keys 尾部。 访问已缓存实例时,将对应键移动到 keys 尾部(标记为最近使用)。 当缓存数量超过 max 时,触发淘汰: 移除 keys 头部的键(最久未使用)。 从 cache 中删除对应实例,并调用该实例的 unmounted 钩子进行清理。 步骤 4:生命周期与缓存状态同步 缓存的组件实例不会触发普通的 unmounted ,而是触发 deactivated 钩子(失活)。激活时触发 activated 钩子。 缓存实例时:从原容器移动到隐藏的容器( storageContainer 的 div 元素),DOM 被隐藏而非销毁。 激活实例时:从隐藏容器移回实际容器。 步骤 5:键的生成与更新 键基于组件的 type 和 key 生成。如果用户为动态组件绑定 key ,缓存会以 key 为区分(例如同一组件不同数据视为不同缓存)。无 key 时则用组件类型标识,可能导致相同类型组件被覆盖缓存。 总结 : KeepAlive 通过 include/exclude 的 name 匹配算法筛选缓存目标,通过 LRU 队列( keys 数组 + max 限制)管理缓存容量,结合 DOM 移动而非销毁的机制实现组件实例的高效复用。