Vue 的响应式更新是一套分阶段协作链路:数据变更触发依赖通知,调度器批量异步执行 render 生成新 VNode,Patch 函数同步比对新旧 VNode 并精准更新 DOM,编译阶段的 PatchFlag 和静态提升进一步加速 diff。

Vue 的响应式更新不是“数据一变,DOM立刻跟着动”,而是一套分阶段、有协作、带调度的链路:数据变化触发依赖通知,依赖通知唤醒渲染函数,渲染函数产出新虚拟节点,最后由 Patch 函数精准更新真实 DOM。整个流程中,响应式系统和渲染器各司其职,又紧密咬合。
响应式系统:从数据变更到 effect 重执行
当你修改 ref 或 reactive 中的值时,Proxy 的 set 拦截器被触发,内部调用 trigger,遍历该属性关联的 Dep(依赖集合),通知所有订阅它的 Effect。组件的 render 函数正是这样一个 Effect,它在首次挂载时就被注册为响应式副作用,并收集了所读取的所有响应式字段作为依赖。一旦依赖变动,这个 Effect 就被标记为“待刷新”,但不会立即运行。
关键点在于:响应式系统只负责“谁该重新跑”,不关心怎么跑、何时跑、跑完干啥——这些由调度器接管。
调度器与 nextTick:批量、异步、有序地执行 render
被标记的 render Effect 不会同步执行,而是被推入一个去重队列。Vue 使用 nextTick(基于 Promise.then 或 MutationObserver)将队列刷新推迟到当前宏任务结束后的微任务阶段。这样能合并同一轮中的多次数据更新,避免重复 render 和 patch。
立即学习“前端免费学习笔记(深入)”;
队列刷新时,按组件父子顺序排序(父先于子),再逐个调用 effect.run()。此时 render 函数重新执行,生成一棵新的 VNode 树——它描述的是你期望的最新 UI 结构。
Patch 函数:只做一件事——同步比对并更新 DOM
Patch 是纯粹的同步操作,它不参与异步调度,也不感知响应式。它只接收两个参数:旧 VNode(上一次 render 的结果,首次挂载时为空)和新 VNode(刚生成的),以及挂载容器。
它的工作包括:
- 若旧 VNode 为空,直接调用 mount 流程,递归创建真实 DOM 元素并插入容器
- 若两者都存在,则进入 diff:先比对节点类型、key、props 等;再递归比对 children,使用双端对比 + 最长递增子序列(LIS)优化移动逻辑
- 仅对差异部分执行 DOM 操作:复用节点、更新 class/style/事件、设置 innerText、调用指令钩子等
- 整个过程不涉及任何异步、队列或生命周期钩子调用——它就是一次确定性的“打补丁”
编译优化如何加速 Patch
在 Vue3 中,模板编译阶段已为 VNode 打上 PatchFlag(如 PATCH_TEXT、FULL_PROPS、DYNAMIC_SLOTS),并把动态节点提取到 Block Tree 中。Patch 阶段可跳过静态节点,只遍历 Block Tree 里的动态节点数组,极大减少比对范围。
例如:<div>{{ msg }}</div> 编译后,div 节点被标记为静态,只有文本子节点被标记为动态。Patch 时直接跳过 div 结构比对,只更新其 textContent。


















