v-once 用于完全静态内容的一次性渲染,v-memo 按固定依赖数组浅比较决定子树是否跳过更新;二者分层配合可高效优化重渲染,但均不提升首次渲染性能。

v-once 和 v-memo 是 Vue 3 中专为减少不必要重渲染而设计的两个轻量但高效的指令,它们作用机制不同,适用场景也明确区分:v-once 用于真正静态的内容,v-memo 用于“局部稳定、条件更新”的子树。
v-once:静态内容的一次性快照
它不是缓存,而是编译期标记。Vue 在生成渲染函数时,会把带 v-once 的节点提升为常量 vnode,后续所有更新周期中完全跳过响应式追踪、diff 对比和 DOM 操作。
- 适合页脚版权、协议文本、固定导航栏、初始化配置展示等整个生命周期都不变的内容
- 子组件、插槽、计算属性、{{ }} 中的函数调用都只执行一次,之后彻底冻结
- 不要对动态 key 使用 v-once,否则 key 变化时无法触发更新(该节点已脱离响应式系统)
- 常见误用:给频繁更新的父组件内某个字段加 v-once —— 它确实能省开销,但前提是这个字段本身真不变
v-memo:按依赖决定是否跳过子树更新
v-memo 是运行时行为,依赖一个数组做浅比较。只有当数组中每个值都与上一次相同时,才跳过该子树的 diff 和 patch;任一变化,就走完整流程并更新缓存。
- 适合大型列表(如表格行、消息项)、高频更新区域(如股票行情)、复杂表单中相对独立的字段组
- 依赖数组长度必须固定,推荐语义化写法,比如 v-memo="[item.id, item.status]" 而非 v-memo="[item]"
- v-memo="[]" 功能等价于 v-once,但语义是“无依赖需检查”,不是“永不变”,两者不可混用在同一元素上
- 它不阻止响应式追踪,子组件内部逻辑照常运行,只是外层控制“要不要重渲染这一块”
搭配使用更高效
在真实项目中,二者常分层配合。例如 shadcn-vue 的数据表格:
- 整个 <header> 和 <footer> 用 v-once,因为布局结构完全静态
- 每行 <tr> 用 v-memo="[row.id, row.updatedAt]",确保仅当关键字段变化才更新整行
- 行内图标或状态标签若完全静态(如固定文字“已发布”),可在其内部再套一层 v-once
注意边界和陷阱
这两个指令不解决首次渲染慢的问题,只优化后续更新开销。使用前需确认:
- 依赖项必须是稳定、可比的基础类型(string/number/boolean),避免传对象引用或 undefined
- v-memo 的数组不能动态增减长度,否则浅比较失效,失去优化效果
- v-once 冻结的是整个 vnode 子树,如果其中意外包含需要响应式更新的部分(比如一个本该监听 count 的按钮),就会出错
- 它们和 <KeepAlive> 解决的问题不同:前者跳过渲染,后者缓存组件实例状态

















