Patch 是 Vue 更新视图的核心机制,通过比对新旧 VNode 实现最小化 DOM 操作,关键在复用与按需更新;sameVnode 判定(tag、key、isComment、isAsyncPlaceholder 全等)决定是否复用节点;patchVnode 处理单节点属性与文本更新,Vue 3 的 patchFlag 进一步优化比对粒度。
Patch 过程是 Vue 更新视图的核心机制,它不重绘整个 DOM,而是通过比对新旧虚拟节点(VNode),精准找出差异,再执行最小化的真实 DOM 操作。关键不在“重写”,而在“复用”和“按需更新”——只要节点可复用(sameVnode 判定通过),就只更新变化的属性、文本或子结构。
Patch 的入口与触发时机
每次响应式数据变更后,组件会重新执行 render 函数生成新 VNode,接着调用
vm.__patch__(prevVnode, vnode)
启动更新流程。这个函数由
createPatchFunction
工厂函数生成,封装了节点操作能力(如 nodeOps)和模块钩子(如 class、style、事件监听器的更新逻辑)。
首次渲染时 oldVnode 是真实 DOM 元素;更新时则是上一次的 VNode。入口统一,但分支逻辑完全不同:
oldVnode 是真实元素 → 走挂载路径:创建新节点、插入父容器、移除旧节点
oldVnode 是 VNode → 进入 diff 主干:先 sameVnode 判断,再 patchVnode 或 updateChildren
sameVnode:复用的前提,不是“看起来像”,而是“能复用”
判断是否走精细化更新,第一关就是
sameVnode(old, new)
。它检查的不是内容,而是节点身份标识:
立即学习
“
前端免费学习笔记(深入)
”;
Vue-router参考手册
Vue-router参考手册下载
下载
标签名(tag)相同
是否为注释节点(isComment)一致
key 值完全相等(
key 是强制复用的关键依据
)
是否为异步占位符(isAsyncPlaceholder)一致
四个条件全满足,才认为“这是同一个节点”,允许复用其真实 DOM 元素(el)。否则直接卸载旧节点、挂载新节点——跳过所有比对,避免错误复用。
patchVnode:单节点的属性与文本更新逻辑
当 sameVnode 为 true,进入
patchVnode
,它负责该节点自身的更新,不含子节点递归(子节点交由 updateChildren 处理)。核心逻辑分三类:
文本节点快捷路径
:若新旧 vnode 都是纯文本(isText === true),直接设置
,不走任何属性比对
非文本节点的属性更新
:遍历
中的 key(如 class、style、on、attrs),逐个调用对应模块钩子(如 updateClass、updateStyle、updateDOMListeners)——这些钩子内部只更新变化的部分,比如 class 只 diff 新旧 classList,不全量 setAttribute
子节点处理策略
:
新旧都有 children → 进入
updateChildren
旧有 children、新无 → 清空 el.innerHTML
旧无 children、新有 → 直接调用 createElm 批量挂载
Vue 3 的 patchFlag:让属性比对更聪明
Vue 3 引入
patchFlag
字段,在编译阶段就标记节点哪些部分可能变化。例如:
:仅需比对 class,忽略 id、style 等静态属性
(带特定 props 名称):只检查绑定的那几个响应式 prop
:说明只有子文本会变,跳过所有子节点结构比对
这意味着 patchVnode 不再盲目遍历全部 data 字段,而是根据 flag 精准命中需要更新的字段,大幅减少无效 diff 开销。这也是为什么 Vue 3 在模板中写
和
能自动获得优化,无需手动加 key 或拆分组件。
el.textContent = newVnode.textnewVnode.dataPatchFlags.CLASSPatchFlags.PROPSPatchFlags.TEXT:class{{ msg }}