Block Tree 是 Vue 3 编译器在模板编译阶段按动态性边界自动划分的块状结构,每个 Block 为最小响应式更新单元,内含共享依赖的动态节点,静态子树被提升复用,运行时仅对 Block 内需更新部分执行精准 diff。

Block Tree 是 Vue 3 编译器在模板编译阶段生成的一种结构化组织方式,它把整个模板按“动态性边界”自动切分成多个独立的响应式更新单元——也就是 Block(块)。每个 Block 只包含可能变化的节点,而其内部的静态子树会被标记为 hoisted(提升),运行时直接跳过比对和遍历。
Block 是什么?为什么需要划分 Block?
Block 是 Vue 3 中最小的、可单独追踪依赖与触发更新的渲染单元。它不是手动写的代码结构,而是编译器根据模板内容自动识别并划分出来的逻辑区块。
- 一个 Block 通常以带有响应式绑定(如 {{ }}、v-if、v-for、:class)的元素为起点,向下包含所有受同一组响应式状态影响的子节点
- 相邻但响应式依赖不同的区域会被划入不同 Block,比如两个 v-if 分支各自形成独立 Block
- 这样做的核心目标是:让 diff 算法不用遍历整棵树,只聚焦于真正可能变化的部分,大幅减少运行时开销
Block Tree 如何配合 PatchFlag 实现精准更新?
PatchFlag 是编译器加在动态节点上的数字标记,用于说明该节点哪类属性会变(如文本、class、style 等)。Block Tree 与 PatchFlag 协同工作:
审查 Vue 管理后台的变更请求(Vue 2 + Element UI,Vue 3 + Element Plus)。重点关注最小化变更实现、上线风险检查以及回归检查清单生成。
- Block 确定“更新范围”——告诉 Vue:“这一小片 DOM 需要重新比对”
- PatchFlag 明确“更新类型”——告诉 diff:“这个节点只需检查文本内容,那个只需比对 class”
- 比如 <p :class="cls">{{ msg }}</p> 会被打上 PatchFlags.CLASS | PatchFlags.TEXT(即 2 | 1 = 3),运行时只做这两项比对,不查 props、style 或子节点结构
静态提升(Static Hoisting)如何与 Block Tree 协同?
静态提升是 Block Tree 能高效工作的前提之一。编译器会把纯静态节点(无任何响应式绑定、指令或插槽)从 Block 内提取出来,在渲染函数外创建一次,后续复用。
立即学习“前端免费学习笔记(深入)”;
- 例如 <div><h2>标题</h2><p>说明文字</p></div> 整个结构若完全静态,就会被提升为常量 VNode,不在 Block 内参与 diff
- 只有包裹它的父容器含动态内容(如 <div :id="id">...)时,才让它成为某个 Block 的根,但其子树仍保持提升状态
- 这减少了内存分配、GC 压力,也缩短了每次 render 函数执行时间
写模板时怎么利于 Block Tree 发挥作用?
你不需要手写 Block,但可以通过编写习惯帮编译器更好划分:
- 避免在静态内容中混入无意义的绑定,比如 <span>{{ '提示文字' }}</span> 应写成 <span>提示文字</span>
- 用 <template v-if> 替代多余 wrapper 元素,减少无效层级干扰 Block 边界识别
- 把强耦合的动态逻辑尽量收敛到局部,比如把分散的 :class 和 {{ text }} 放在同一元素上,比拆到父子元素中更易形成紧凑 Block
- 慎用 v-html 或内联函数调用,它们会让编译器难以静态分析,可能退化为 FULL_PROPS 类型 Block,削弱优化效果


















