vue编译器不参与响应式更新或patch过程,仅负责将模板编译为渲染函数;patch由运行时渲染器执行,基于新旧vnode树比对,通过双端指针等策略最小化dom操作。

Vue.js 编译器本身不参与响应式数据更新或 Patch 过程。需要明确:Patch 是运行时渲染器(renderer)的行为,不是编译器(compiler)的职责。
Vue 的编译器(如 @vue/compiler-core)只在构建阶段或运行时模板编译阶段工作,负责将 <template></template> 转为渲染函数(render function),生成可执行的 JavaScript 代码。它输出的是「如何生成 VNode」的逻辑,不处理数据变化、不执行 diff、不操作 DOM。
真正承担响应式更新与 Patch 策略的是 Vue 的运行时核心(runtime-core)和虚拟 DOM 渲染器(runtime-dom)。以下是关键分工与实际策略:
Patch 策略由运行时 renderer 主导,非编译器
- 编译器生成的 render 函数,会读取响应式数据(如
proxy.count)并构造 VNode 树; - 当响应式数据变更,触发组件的
effect重新执行,调用该 render 函数产出 新 VNode; - 此时,
renderer.patch(prevVNode, nextVNode)被调用,进入标准 patch 流程; - 编译器早已完成工作,对后续更新无感知、不干预。
Patch 的核心策略聚焦于 VNode 树比对,而非数据本身
- 不对比 props 对象或 data 值,而是对比由新旧数据渲染出的 VNode 结构;
- 使用
isSameVNodeType(n1, n2)判断是否复用节点(依赖type+key); - 若节点可复用,则递进调用
patchProps()、patchChildren()等精细化更新; - 子节点列表采用双端指针算法(
updateChildren),最小化 DOM 移动与重排。
编译器间接影响 Patch 效率的方式
虽然不执行 patch,但编译器可通过以下方式优化最终 patch 行为:
-
静态提升(hoist static):把模板中完全静态的节点(如
<div class="logo">)提升为常量,避免每次 render 重建,减少 VNode 创建开销;<li> <strong>缓存事件处理器(cache handlers)</strong>:对不依赖响应式变量的内联事件(如 <code>@click="doSomething")生成一次性函数,避免每次 patch 时重复绑定; -
v-once / v-memo 指令编译支持:生成带标记的 VNode(如
patchFlag: PatchFlags.BAIL),让 runtime 在 patch 阶段跳过比对; -
key 的静态分析与警告提示:在编译期检查
v-for中 key 的合法性,避免因 key 错误导致低效 patch 或状态错乱。
响应式更新驱动 patch 的真实链路
data.count++ → proxy setter 触发 effect 更新 → 组件 render 函数执行 → 生成 new VNode → renderer.patch(oldSubTree, newSubTree) → sameVnode? → yes → patchVnode() → updateChildren() → DOM 更新
整个过程里,编译器只贡献了中间那个「render 函数」——它是静态生成的、无状态的代码载体;而 patch 是动态的、有状态的、逐帧执行的 DOM 同步机制。
不复杂但容易忽略:所谓“编译器的 Patch 策略”,本质上是个常见误解。真正决定更新行为的,是运行时 renderer 如何解释 VNode、如何调度 DOM 操作,而编译器只是帮它写得更高效、更安全。










