v-once 是编译期静态快照,首次渲染后完全脱离响应式系统;v-memo 是运行时依赖比对,仅当指定依赖未变时跳过更新。二者适用场景不同:内容永不变用 v-once,局部按明确字段更新用 v-memo。

v-once 和 v-memo 都是为了减少 Vue 渲染开销而设计的指令,但它们介入的时机、控制粒度和适用逻辑完全不同。用错一个,可能不仅没提速,反而让更新行为变得不可预期。
v-once:编译期就“定格”的静态快照
它不是运行时做判断,而是编译阶段就把带 v-once 的节点识别为静态内容,直接提升为常量 vnode。这意味着:
- 首次渲染后,该节点完全脱离响应式系统——不收集依赖、不触发 effect、不参与任何后续更新流程
- 哪怕里面写了 {{ someExpensiveMethod() }} 或嵌套了子组件,也只执行一次,之后永远复用初始结果
- 适合真正不变的内容:页脚版权文字、初始化协议条款、固定导航栏结构(如 shadcn-vue 的 MainNav)
- 注意:不能用于需要后期更新的区域;若需动态切换,应配合 v-if 控制显隐,而非强行移除 v-once
v-memo:运行时靠依赖数组决定“跳不跳”
它不冻结内容,而是每次更新前做一次浅比较,根据传入的依赖数组决定是否跳过 diff 和 patch:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 依赖数组必须稳定可比:推荐用 primitive 类型(id、status、timestamp),避免对象引用或 undefined
- 数组长度要固定,每个位置语义明确,比如 v-memo="[item.id, item.status]" 而非 v-memo="[item]"
- 适用于局部稳定、整体高频更新的场景:表格行、聊天消息列表、股票行情卡片
- v-memo="[]" 等价于跳过所有依赖判断,效果类似 v-once,但语义是“无变化条件”,不是“永不变”
别踩的坑:边界与误用
这两个指令都只优化“更新阶段”,对首次渲染无加速作用。常见误区包括:
立即学习“前端免费学习笔记(深入)”;
- 给带动态 key 的元素加 v-once:key 变了,但 v-once 让它仍复用旧 vnode,导致 DOM 错乱
- 在 v-for 中对整个列表容器用 v-memo,却忽略 :key —— key 缺失会导致复用错误,v-memo 也救不了
- 把 v-once 和 v-memo 同时写在一个元素上:Vue 会忽略 v-memo,且编译警告
- 用 v-memo 包裹含副作用的计算属性或 watch:依赖没变时跳过渲染,但副作用本该执行,结果被静默跳过
怎么选?看数据变不变、谁在变
一句话判断:
- 内容从不更新 → 用 v-once
- 内容会更新,但只由几个明确字段驱动 → 用 v-memo 并列清这些字段
- 字段本身不稳定(比如每次返回新对象)→ 先 normalize 数据结构,再考虑 v-memo
- 想缓存组件状态(比如表单填了一半)→ 换
,不是这两个指令的职责


















