Vue.js的Patch算法仅在响应式数据变化后、新VNode生成完毕并进入_update阶段时触发,入口为updateComponent调用_vm._update(vm._render(), hydrating),首次挂载全量创建DOM,后续更新才执行diff+patch。

Vue.js 的 Patch 算法不是随时运行的,它只在组件真正需要更新视图时才触发——核心时机是响应式数据变化后、新 VNode 生成完毕、进入 _update 阶段的那一刹那。理解这个时机,才能把优化落在刀刃上。
Patch 执行的准确入口:从 updateComponent 开始
当响应式数据变更,触发依赖通知,组件的渲染 watcher 就会执行 updateComponent 方法:
- 该方法内部调用
vm._update(vm._render(), hydrating) -
_render()生成新的 VNode 树;_update()则调用vm.__patch__(prevVnode, vnode) - 首次挂载时
prevVnode为 null,直接走createElm全量创建 DOM - 后续更新才真正进入 diff + patch 流程,也就是我们常说的“打补丁”
影响 Patch 效率的关键变量:PatchFlag 与 Block Tree
Vue 3 的 Patch 不再盲目遍历整棵树,而是靠编译阶段注入的 PatchFlag 定位动态节点,大幅缩小比对范围:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 静态内容(如固定 class、纯文本)会被提升(hoistStatic),运行时不参与 diff
- 模板中显式写出
class="btn"比:class="['btn']"更易被标记为 CLASS 类型 flag - 使用
v-memo可手动缓存子树,例如<div v-memo="[count, filter]>,依赖未变则跳过整个块的 patch - 开启构建时 tree-shaking,移除未使用的编译模块(如 baseCompile),减少运行时体积
避免无效 Patch:控制响应式粒度与 key 策略
很多性能问题其实不来自 diff 本身,而来自不该触发的 patch:
立即学习“前端免费学习笔记(深入)”;
- 列表项必须用稳定唯一 key(如
item.id),禁用index,否则移动/插入会引发全量重排 - 深层对象属性访问(如
user.profile.address.city)会扩大依赖范围,改用toRef(user, 'profile')精准订阅 - 大对象(富文本、上传文件元数据)用
markRaw包裹,阻止 Proxy 递归劫持,避免无谓的 getter/setter 开销 - 纯展示组件(图标、静态卡片)可声明为
functional: true,剥离响应式系统和生命周期
真实 DOM 落地前的最后一关:减少 layout 与 repaint
Patch 最终要操作真实 DOM,而频繁的样式读写、强制同步布局会拖垮性能:
- 避免在 patch 过程中读取
offsetHeight、getComputedStyle等触发重排的属性 - 批量修改样式优先用 CSS class 切换,而非逐个设置
style.xxx - 长列表务必启用虚拟滚动(virtual-scroll),仅挂载可视区域约 20–30 项,其余用占位元素撑开高度
- 动画类操作尽量交由 CSS transform/opacity 实现,避开触发布局计算的属性(如 width、top、left)


















