block tree 是 vue 3 编译器在构建阶段实施的语义分块策略,非运行时数据结构;它将模板切分为多个动态更新单元(block),每个 block 以共享响应式依赖的节点为边界,仅对 dynamicchildren 数组内节点执行 diff,配合静态提升与 patchflag 实现精准、高效更新。

Block Tree 不是运行时的数据结构,而是 Vue 3 编译器在构建阶段对模板做的语义分块策略。它的核心作用很直接:把整棵虚拟 DOM 树切分成若干“动态更新单元”,让 diff 只聚焦于真正可能变化的部分,跳过所有静态内容和稳定结构。
Block 是什么?不是节点,而是更新边界
一个 Block 是编译器识别出的、具有相同响应式依赖的一组节点所构成的逻辑块。它通常以带绑定的元素(如 {{ msg }}、v-if、:class)为起点,向下包含所有受同一组响应式状态影响的子节点。
- 纯静态节点(如
<h2>标题</h2>)不会单独成 Block,而是被提升复用,根本不出现在 render 函数里 - 相邻但依赖不同响应式变量的区域,会被划入不同 Block,比如两个
v-if分支各自独立 - 每个 Block 在运行时对应一个
createBlock()调用,内部维护dynamicChildren数组,只存真正需要比对的节点
PatchFlag 与 Block 协同:查表式更新
Block 确定“更新范围”,PatchFlag 明确“更新类型”。两者配合,让 diff 不再是逐属性扫描,而是查表定位。
审查 Vue 管理后台的变更请求(Vue 2 + Element UI,Vue 3 + Element Plus)。重点关注最小化变更实现、上线风险检查以及回归检查清单生成。
-
<p :class="cls">{{ msg }}</p>会被打上PatchFlags.CLASS | PatchFlags.TEXT(即数值 3),运行时只检查 class 和文本内容 - 没有 PatchFlag 的节点(如静态子树)完全不参与 diff
- 同一个 Block 内多个节点共享依赖,可批量触发更新,减少依赖追踪开销
静态提升是 Block Tree 高效的前提
静态提升不是可选项,而是 Block 能起作用的基础。编译器会把完全静态的节点提前创建并缓存,后续渲染直接复用。
- 例如
<div> <h2>说明</h2> <p>详情</p> </div>若无任何绑定,整个结构会被提升为常量 VNode - 即使它被包裹在动态容器中(如
<div :id="id">...</div>),其子树仍保持提升状态,不进入 Block 的 diff 流程 - 这显著降低内存分配频率和 GC 压力,也让每次 render 函数执行更轻量
怎么写出利于 Block Tree 的模板?
优化不靠手动写 Block,而在于让编译器能准确识别静态与动态边界。
- 避免在插值中调用复杂函数:
{{ formatTitle(item) }}会让编译器无法静态分析,可能退化为全量更新 - 用
<template v-if="cond"></template>替代元素级v-if,更容易触发独立 Block 创建 - 长列表配合
key + v-memo,显式声明依赖数组,可跳过整个 Block 的更新判断 - 拆分大型组件——每个子组件天然是一个 Block,实现更新隔离
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!









