v-memo的核心是跳过虚拟dom子树的diff和patch过程,而非跳过渲染;它在编译阶段标记memo区域并生成依赖比对逻辑,运行时通过浅层引用比较决定是否复用缓存vnode。

v-memo 的核心不是“跳过渲染”,而是“跳过虚拟 DOM 子树的 diff 和 patch 过程”——它在编译和运行时协同工作,从 vnode 构建阶段就切断更新链路。
编译阶段:标记 memo 区域并生成依赖比对逻辑
Vue 3 编译器(@vue/compiler-dom)在遇到 v-memo 指令时,会将该节点及其整个子树标记为 memo 类型。模板:
{{ name }}
会被编译为类似这样的 render 函数片段:
const memo = [id, name];if (cache && cache[0] === memo[0] && cache[1] === memo[1]) {
return cache[2]; // 直接返回缓存的旧 vnode
} else {
const vnode = createVNode('div', ..., [...]);
cache = [id, name, vnode];
return vnode;
}
注意:编译器会把依赖数组字面量(如 [id, name])提取为独立变量,并在生成代码中插入浅比较逻辑(===),不调用任何响应式追踪函数(如 track),因为依赖值本身已由父作用域提供。
运行时:vnode 创建即缓存,diff 阶段完全绕过
当组件触发更新时,v-memo 节点对应的 processElement 或 processComponent 不会进入标准 diff 流程。它的处理路径是:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 先执行依赖数组的逐项
===比较(仅浅层引用相等) - 若全部相等,直接返回上一次缓存的
cachedVNode,且该 vnode 的patchFlag被设为0(表示不可变),子节点也不再递归 patch - 若任一依赖变化,则新建完整 vnode 树,并替换缓存 —— 此过程与普通渲染无异,但仅发生于变化时刻
关键点在于:这个缓存发生在 render() 函数内部,属于“render-time memoization”,不涉及 effect 依赖收集,也不影响响应式系统,纯粹是 vnode 层的复用决策。
与 v-once、computed、React.memo 的本质区别
v-once 是单次渲染后永久冻结,不检查依赖;computed 是响应式计算缓存,用于 JS 值;React.memo 是组件级 props 浅比较,需手动包裹函数组件。
v-memo 是唯一一个直接作用于模板子树、无需额外组件封装、且在 vnode 构建阶段就完成复用判断的机制。它不改变组件实例生命周期,也不阻止事件监听器或 ref 更新 —— 它只让 Vue 在 patch 阶段“看不见”这一块子树。
为什么必须配合稳定 key 和 v-for 使用?
在列表中,v-memo 通常写在 v-for 内部元素上,例如:
这里 :key 保证了 vnode 的复用位置正确,而 v-memo 决定该位置上的子树是否重 render。如果 key 不稳定(如用 index),即使依赖未变,vnode 也可能被错误地移动或丢弃,导致缓存失效或状态错乱。空依赖数组 v-memo="[]" 等效于 v-once,但仍是每个列表项独立缓存,而非整列表冻结。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!









