Vue3 keep-alive核心源码的解析

在 Vue 3 中,<keep-alive> 组件的源码实现比较复杂,但主要涉及的是组件实例的缓存、生命周期钩子的管理、以及缓存策略。以下是对其核心源码的解析。

1. KeepAlive 的核心结构

<keep-alive> 组件的核心在于它如何管理组件实例的缓存,以及如何在组件被复用和销毁之间做切换。以下是 KeepAlive 的基本实现结构。

javascript 复制代码
export const KeepAlive = {
  name: 'KeepAlive',
  __isKeepAlive: true, // 标识为 KeepAlive 组件

  props: {
    include: [String, RegExp, Array],
    exclude: [String, RegExp, Array],
    max: [String, Number]
  },

  setup(props, { slots }) {
    // 缓存和最近使用缓存的 key 队列
    const cache = new Map();
    const keys = new Set();

    // 当前激活的组件实例
    let current = null;

    // 返回虚拟节点的 render 函数
    return () => {
      const vnode = slots.default();
      if (!vnode) return null;

      const name = vnode.type.name || vnode.key;
      if (name && shouldCache(name, props.include, props.exclude)) {
        if (cache.has(vnode.key)) {
          vnode.component = cache.get(vnode.key).component;
          // 更新 key 的使用顺序
          keys.delete(vnode.key);
          keys.add(vnode.key);
        } else {
          cache.set(vnode.key, vnode);
          keys.add(vnode.key);

          // 如果超过缓存数量限制,则移除最早使用的缓存
          if (props.max && keys.size > parseInt(props.max, 10)) {
            const firstKey = keys.values().next().value;
            cache.delete(firstKey);
            keys.delete(firstKey);
          }
        }

        vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE;
      }

      current = vnode;
      return vnode;
    };
  }
};

2. KeepAlive 的核心逻辑

KeepAlive 的源码中,以下几个核心部分需要关注:

a. 缓存机制(cachekeys
  • cache 是一个 Map 对象,用于存储被缓存的组件实例。
  • keys 是一个 Set 对象,记录了缓存组件的 key 顺序,便于管理缓存的顺序。
  • vnode.key 被用作缓存的 key,确保每个实例的唯一性。
b. shouldCache 函数

shouldCache 用于判断当前组件是否符合缓存的条件。它会检查 includeexclude 属性,决定是否要缓存该组件。

javascript 复制代码
function shouldCache(name, include, exclude) {
  if (include && !matches(include, name)) {
    return false;
  }
  if (exclude && matches(exclude, name)) {
    return false;
  }
  return true;
}

function matches(pattern, name) {
  if (Array.isArray(pattern)) {
    return pattern.includes(name);
  } else if (typeof pattern === 'string') {
    return pattern.split(',').includes(name);
  } else if (pattern instanceof RegExp) {
    return pattern.test(name);
  }
  return false;
}
  • include:要缓存的组件列表。
  • exclude:不缓存的组件列表。
c. 缓存的更新和删除

当缓存的数量超过 max 限制时,会移除最早使用的缓存。这是通过 LRU(最近最少使用)策略实现的:

javascript 复制代码
if (props.max && keys.size > parseInt(props.max, 10)) {
  const firstKey = keys.values().next().value;
  cache.delete(firstKey);
  keys.delete(firstKey);
}
  • 每次访问组件时,都会更新 keys 的使用顺序,以保证 keys 最前面的始终是最早使用的组件。
  • 如果 keys.size 超过 max,则会删除最早的 key 和对应的缓存。

3. 生命周期钩子的管理

在 Vue 3 中,KeepAlive 会触发组件的 activateddeactivated 生命周期钩子。以下是相关的核心代码:

  • activated:当缓存组件重新显示时调用。
  • deactivated:当组件被移除但未销毁时调用。
javascript 复制代码
// KeepAlive 组件的生命周期处理
function onActivated(instance) {
  if (instance.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
    queuePostRenderEffect(() => {
      instance.isDeactivated = false;
      if (instance.a) {
        invokeArrayFns(instance.a);
      }
    }, instance.parent);
  }
}

function onDeactivated(instance) {
  if (instance.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
    queuePostRenderEffect(() => {
      instance.isDeactivated = true;
      if (instance.da) {
        invokeArrayFns(instance.da);
      }
    }, instance.parent);
  }
}
  • onActivated :在 KeepAlive 缓存的组件被重新渲染时,会调用该函数,使组件的 isDeactivated 状态为 false,并执行 activated 钩子。
  • onDeactivated :当组件被移除但未销毁时调用 onDeactivated,将 isDeactivated 状态设置为 true,并调用 deactivated 钩子。

4. 总结

<keep-alive> 的核心逻辑包括:

  • 缓存策略 :通过 cachekeys 实现 LRU 策略管理。
  • 条件判断 :通过 includeexclude 判断是否缓存组件。
  • 生命周期钩子 :控制缓存组件的 activateddeactivated 状态。

KeepAlive 组件的实现充分考虑了缓存的性能和灵活性,允许开发者通过配置来控制组件缓存的行为,有效地提升了应用的性能。

相关推荐
Cutecat_2 小时前
视频字幕处理工具横向:提取模式 vs 编辑模式,该如何选择
android·前端·ios·语音识别
qq_422152572 小时前
PDF 加水印工具怎么选?2026 年文档版权保护方案对比
前端·pdf·github
kyriewen2 小时前
手写 Promise.all、race、any:不到 30 行代码,解决并发异步的所有姿势
前端·javascript·面试
brucelee1863 小时前
OpenClaw 浏览器控制(Chrome MCP)完整教程
前端·chrome
ct9783 小时前
React 状态管理方案深度对比
开发语言·前端·react
胡志辉的博客3 小时前
深入浅出理解浏览器事件循环:从一道输出题讲到 Chrome 源码
前端·javascript·chrome·chromium·event loop
代码不加糖4 小时前
js中不会冒泡的事件有哪些?
前端·javascript·vue.js
懂懂tty4 小时前
Vue2与Vue3之间API差异
前端·javascript·vue.js
AI焦点4 小时前
跨越协议鸿沟:Tool Use状态机从Anthropic到OpenAI兼容体系的适配要点
前端·人工智能
Dxy12393102164 小时前
Python线程锁:为什么多线程会“打架“,以及怎么解决
开发语言·前端·python