Vue.js 中不存在名为“BlockTree”的官方概念或数据结构,v-once 的实现依赖编译时标记为 STATIC Block 及运行时跳过 patch 流程,而非树形管理。

Vue.js 中并没有 BlockTree 这个公开的、用户可直接操作或配置的概念,也**不存在官方文档或源码中定义的 “BlockTree” 数据结构**来专门处理 v-once 节点。
实际机制:v-once 的实现基于 Block 和 PatchFlag
v-once 的核心行为是「渲染一次,之后跳过更新」。Vue 3 的响应式更新系统(尤其是 DOM diff 算法)通过以下方式实现它:
-
编译阶段标记为静态块(Static Block):带
v-once的元素及其子树,在模板编译时会被识别为「静态内容」,生成一个独立的block(即createBlock调用),并附带PatchFlags.STATIC标志。 -
运行时跳过 patch 流程:当组件重新渲染时,diff 算法遇到
PatchFlags.STATIC的 block,会直接复用上一次创建的 DOM 节点,完全跳过 props 更新、子节点 diff、指令更新等所有更新逻辑。 -
不参与响应式依赖收集:
v-once内部的表达式(如{{ msg }})在首次渲染后不会被追踪,后续msg变化也不会触发该节点重渲染。
v-once 不等于“永久静态”,而是“单次渲染 + 持久复用”
需注意几个常见误解:
-
v-once节点仍属于组件 VNode 树的一部分,只是更新时被跳过;它依然能响应父组件的v-if/v-show控制显隐,也能被teleport移动。 - 如果
v-once包裹的内容本身含响应式子组件(比如自定义组件),该子组件**内部仍会响应更新**——v-once只影响当前层级的 VNode 创建/patch 行为,不递归冻结子组件逻辑。 - 没有所谓 “BlockTree” 结构来维护这些节点;Vue 内部使用扁平化的 block 链表(
currentBlock栈)管理嵌套 block,用于高效卸载和更新,但这是实现细节,不暴露给用户。
如何验证 v-once 的行为
可在开发模式下观察:
立即学习“前端免费学习笔记(深入)”;
- 打开 Vue Devtools → 查看组件的
render函数输出,带v-once的节点通常出现在createBlock(...)调用中,且无dynamicChildren或patchFlag为1 /* TEXT */等动态标识。 - 修改响应式数据后,用浏览器开发者工具检查对应 DOM 是否真实未变动(包括 class、text、attr 等),确认未触发 patch。
简言之,Vue 用编译时标记 + 运行时短路策略实现 v-once,背后是 block 机制与 patch flag 协同工作,而非某种叫 “BlockTree” 的树形管理结构。










