createBlock 的核心作用是构建具备动态节点追踪能力的 Block vnode,通过 dynamicChildren 数组显式隔离需响应式更新的带 patchFlag 节点,跳过静态子树 diff;其依赖 openBlock 开启收集上下文,并借助栈式机制实现嵌套 Block 的动态节点隔离。

createBlock 的核心作用不是创建普通 vnode,而是构建一个具备动态节点追踪能力的 Block vnode——它通过 dynamicChildren 显式隔离出真正需要响应式更新的部分,从而跳过静态子树的 diff。
Block 的本质是带收集能力的 vnode 容器
普通 vnode 只描述结构和属性;Block vnode 多了一个关键字段:dynamicChildren,它是一个数组,只存那些带 patchFlag 的子代 vnode(包括直接子节点、孙子节点等所有后代中的动态节点)。这个数组在 patch 阶段会被运行时直接遍历更新,完全绕过整棵子树的递归 diff。
例如模板:
静态内容
{{ msg }}
编译后, 调用 注意:如果传入 不是所有带 关键点在于:vnode 创建函数(如 当模板中出现嵌套动态结构(比如 这样就保证了每个 Block 的 createBlock 生成的 vnode 的 dynamicChildren 就只包含 和 patchFlag: TEXT 和 patchFlag: CLASS 的节点,而 被彻底排除在外。openBlock 是动态收集的开关
createBlock 前,必先执行 openBlock()。它的逻辑非常轻量:
blockStack
currentBlock)推入栈顶currentBlock 为一个新空数组,作为本次 Block 的收集容器disableTracking = true(比如在静态提升节点或某些特殊指令中),currentBlock 会被设为 null,意味着本次不收集任何动态子节点。动态节点如何进入 dynamicChildren?
patchFlag 的 vnode 都能进 dynamicChildren。必须同时满足两个条件:
patchFlag > 0(即非静态,如 PatchFlags.TEXT、CLASS 等)currentBlock 不为 null,且处于当前 Block 的收集上下文中createVNode)内部会检查 currentBlock。若存在,就将自身 push 进去——这个动作是隐式的、自动发生的,开发者无需手动操作。嵌套 Block 的收集是栈式隔离的
v-if 内套 v-for),就会产生 Block 嵌套。此时:
openBlock → 推入第一个收集数组 AopenBlock → 推入第二个收集数组 B,currentBlock 指向 BcreateBlock 执行完毕 → 调用 closeBlock(内部弹出 B,currentBlock 恢复为 A)dynamicChildren 严格对应其作用域内的动态节点,互不干扰。










