人人都会AI编程

v-once、v-memo 指令优化

更新时间:2026-07-11

在 Vue 的默认更新机制下,当一个组件重新渲染时,它的整个模板会重新执行,生成新的虚拟 DOM 树并与旧树对比。绝大多数情况下,这已经足够高效——但如果你明确知道某一部分内容永远不会再改变,或者只依赖于特定的几个数据,就可以用 v-oncev-memo 来跳过这些部分的重复渲染,进一步压榨性能。

v-once:只渲染一次,永不更新

v-once 是一个“一次性的”指令。它告诉 Vue:这个元素及其所有子节点,只在首次渲染时创建,后续任何数据变化都不要再动了

典型场景

  • 展示一段后端返回的静态协议文本、公告内容、用户协议,这些内容一旦渲染就永远不会变。
  • 列表的标题、表头等纯静态结构。
  • 第三方富文本编辑器的预览区,内容只会在提交时一次性展示。

用法

<div v-once>
  <h1>{{ pageTitle }}</h1>
  <p>{{ staticContent }}</p>
</div>

上述模板中,pageTitlestaticContent 的初始值会被渲染到 DOM 中,之后即使你修改了响应式数据 pageTitlestaticContent,这个 <div> 里的内容不会发生任何变化——Vue 会把它当作一个“静态块”,在后续的 Diff 中直接跳过。

内置的原理
v-once 会让编译器将该节点的 VNode 标记为 isOnce: true,并且在渲染时将虚拟节点缓存起来。后续组件更新进入 Diff 阶段时,当遇到标记为 isOnce 的节点,Vue 会直接复用旧的 VNode 和对应的真实 DOM,不触发任何对比。

注意事项

  • 只适用于真正永不变化的内容,如果里面的数据后续可能因为用户操作而改变,请勿使用 v-once
  • 过度使用意义不大,因为 Vue 的编译优化已经能够自动跳过纯静态内容。v-once 更多用于“内容本身依赖响应式数据,但业务上确定它不应该变”的场景。
  • v-once 会锁定整个子树,即使子树里有自己的响应式状态(比如一个带有自己内部状态的子组件),也不会再更新。

v-memo:按条件缓存子树

v-memo 是 Vue 3.2+ 引入的更精细缓存指令。它接收一个依赖数组,当数组中的所有依赖值都没有变化时,该节点及其子树将完全跳过 Diff,直接复用上一次的渲染结果。

典型场景

  • 一个超长的列表项,只有当该列表项的数据本身变化时才需要更新,其他无关数据变化时,整个列表项应保持不变。
  • 一个大组件中有多个相互独立的区域,每个区域只依赖于特定的几个状态,用 v-memo 可以防止一个区域更新时连累另一个区域不必要的对比。

用法

<div v-memo="[item.name, item.price]">
  <span>{{ item.name }}</span>
  <span>¥{{ item.price }}</span>
</div>

只有当 item.nameitem.price 发生变化时,这个 <div> 内部才会重新渲染;如果只是 item.image 变了(不在依赖数组中),或者列表中的其他项变了,这个区域会直接使用缓存,零开销。

更强大的用法:结合 v-for
v-for 内部,v-memo 可以显著提升长列表的更新性能:

<div v-for="item in list" :key="item.id" v-memo="[item.selected]">
  <span>{{ item.name }}</span>
  <input type="checkbox" v-model="item.selected">
</div>

这里 v-memo 的依赖是 [item.selected]。当你勾选了某一项,导致该 item.selected 变化,只有这一项会更新,其余未选中的项不仅跳过 DOM 更新,连虚拟节点的对比都直接跳过。这比仅靠 key 的 Diff 优化更彻底,因为 key 只保证节点复用,仍然要进行属性对比。

原理
在渲染时,Vue 会检查 v-memo 的依赖数组是否与上一次渲染时的值完全相同(浅比较)。如果相同,则直接返回缓存的 VNode,完全跳过该子树的新 VNode 创建和 Diff 过程。

注意事项

  • 依赖数组应尽可能精简且稳定,不要写 v-memo="[someObject]"(整个对象引用)除非对象引用真的不变,因为浅比较容易失效。
  • v-memo 也应搭配 key 使用,在 v-for 中确保节点复用。
  • v-once 不同,v-memo 是有条件缓存:当依赖变化时,它会正常更新,不会永久卡死。
  • 不要滥用,只在确实有可测量的性能瓶颈时才使用。大多时候 Vue 自身的 Diff 优化已经足够,加入 v-memo 反而增加心智负担。

如何选择

  • 如果内容确定一次性生成且永不再变,用 v-once
  • 如果需要根据某几个特定条件决定是否跳过更新(尤其在长列表项中),用 v-memo

两者都是“手动告诉编译器可以跳过某些工作”的指令,它们依赖开发者对业务逻辑的准确判断。用好它们,可以在极低开发成本下,把列表渲染、大量静态内容的更新性能推至极致。