v-memo 跳过整棵子树的虚拟 dom diff 和 patch,包括子组件 vnode 比对、props 更新判断及生命周期钩子触发,但子组件内部响应式逻辑仍有效;其是否更新取决于外层 v-memo 依赖是否变化。

v-memo 指令本身不跳过子组件的渲染更新,它跳过的是整个子树的虚拟 DOM diff 和 patch 过程——包括子组件的 vnode 比对、props 更新判断、生命周期钩子触发(如 updated)、以及 DOM 操作。但要注意:子组件内部的响应式逻辑依然有效,只是外层“决定不重绘它”。
v-memo 如何影响子组件?
当你在包裹子组件的外层元素上使用 v-memo,例如:
<div v-memo="[item.id, item.status]"> <usercard :user="item"></usercard><statusbadge :status="item.status"></statusbadge> </div>
- ✅ 如果
item.id和item.status都没变 → Vue 直接复用上次生成的整棵 vnode 子树,不会重新创建UserCard和StatusBadge的新 vnode,也不会调用它们的update流程。 - ❌ 如果任一依赖变化 → 正常走完整更新流程,子组件会收到新 props 并可能触发
updated、beforeUpdate等。
也就是说:子组件是否“渲染更新”,取决于它被包裹的 v-memo 区域是否命中缓存,而不是子组件自己有没有 shouldUpdate 之类逻辑。
为什么子组件内部仍可能“动”?
v-memo 不冻结子组件内部状态:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 子组件自己的
ref、computed、定时器、事件监听等照常运行; - 它的
setup或data中定义的响应式变量,只要被访问或修改,effect 依然触发; - 只是当父级
v-memo命中缓存时,父级不会把新 props 传下去,也不会触发子组件的更新周期。
举个例子:
<template><div v-memo="[user.id]">
<userprofile :user="user"></userprofile><button>{{ count }}</button>
</div>
</template><script setup>
const count = ref(0)
const user = reactive({ id: 1, name: 'Alice' })
</script>
- 点击按钮只增加
count,但user.id没变 →UserProfile完全不更新(哪怕它内部用了watch或onUpdated); -
count的变化只影响<button></button>文本,而该节点在v-memo外层?不,它在内层 —— 所以整个<div> 被 memo 封装了,<code>count更新会触发父组件重渲染,但v-memo判断后发现[user.id]没变,于是跳过整块,连按钮文本都不会刷新。⚠️ 注意:
count是响应式变量,但它在v-memo内部,所以它的变化也会被“拦住”——v-memo控制的是整块模板子树,不是只针对子组件。
和子组件自身优化的关系
-
v-memo是父级视角的粗粒度跳过,不替代子组件内部的优化手段(比如defineAsyncComponent、shallowRefprops、v-once局部静态内容); - 若子组件本身开销大,又经常被
v-memo跳过,那它的初始化成本(首次 mount)仍存在,但后续更新被省了; - 不建议在子组件内部再套
v-memo(除非它自己也渲染长列表),容易嵌套混乱且无收益。
实际建议:怎么让子组件真正“少更新”
- 把稳定字段单独提取为
v-memo依赖,比如v-memo="[user.id, user.avatarHash]",避免传整个user对象; - 子组件接收
props时,用shallowRef或toRefs拆解,防止深层响应式拖慢比对; - 对子组件内完全静态的部分(如图标、固定文案),可加
v-once,和v-memo分层配合; - 避免在
v-memo依赖里写函数调用、计算属性 getter、或Date.now()这类每次返回新值的表达式。
它不是魔法开关,而是告诉 Vue:“这部分结构,只要这几个值没动,就别碰。” 子组件是否更新,由这个指令统一裁决。
-










