Diff算法按“自顶向下、深度优先”逐层patch,父组件先完成自身VNode比对并更新props,再递归触发子组件render与diff,子组件VNode仅在父patch到达时才生成和比对。

Diff 算法在组件更新时,并不是一次性比对整个树,而是按“自顶向下、深度优先”的顺序,逐层进入子组件并执行 patch。父子组件的 VNode 比对严格遵循“父先于子”——父组件的 render 生成新 VNode 后,先完成自身 VNode 与旧 VNode 的 diff(包括 props、class、style 等静态属性和动态绑定),再递归调用子组件的 patchComponent 或 processComponent,进而触发子组件自身的 diff 流程。
父组件先完成自己的 patch,再处理子组件
当父组件响应式数据变化导致重新渲染时:
- 父组件执行
render()得到新的父 VNode - 框架调用
patch(oldVNode, newVNode)对父 VNode 进行比对 - 若发现子节点是组件类型(
vnode.type是对象且含setup/render),则进入组件处理逻辑(如patchComponent) - 此时不会立即比对子组件的内部结构,而是先更新父组件的组件实例(如更新 props、触发
updateChildComponent)、判断是否需要卸载/复用/挂载子组件实例 - 只有子组件实例确定复用后,才在其
update钩子中触发子组件自身的render和后续 diff
子组件的 diff 发生在父流程的“递归下降”中
子组件的 VNode 比对不是独立并行的,而是嵌套在父组件 patch 的调用栈里:
- 父组件的
patch在处理到某个子组件 VNode 时,会调用processComponent -
processComponent内部判断子组件实例是否存在;若存在且可复用,就调用updateComponentPreRender更新其 props、slots,并触发子组件重新render - 子组件
render产出新的子 VNode 后,立刻进入子组件自己的patch流程(即patch(childOldVNode, childNewVNode)) - 这个过程会继续向下递归,直到遇到普通元素或文本节点,才真正执行“双端 diff”或“快速路径”比对
关键点:diff 不等于“同时比对所有组件”
很多人误以为 diff 是把整棵树的 VNode 拿出来统一比较。实际上:
- VNode 树是惰性构造的:子组件的 VNode 只在父组件 render 中执行
h(Comp)时才生成 - 比对是“需求驱动”的:父组件 patch 到哪,才生成并比对对应的子 VNode
- 组件边界就是 diff 的递归边界:父组件 patch 函数不会直接比对子组件内部 children,而是交由子组件实例自己的 patch 逻辑处理
- 所以父子更新有明确时序:父 props 更新 → 父 re-render → 父 patch → 子组件 update 触发 → 子 re-render → 子 patch
一个简单例子帮你理清顺序
假设模板:text 改变:
- Parent 响应式依赖触发,执行
Parent.render()→ 生成新 Parent VNode,其中包含一个 Child 类型的子 VNode - patch 开始比对 Parent VNode:发现子节点是组件,进入
processComponent - 查到已有 Child 实例,调用
updateComponentPreRender更新其props.msg - Child 实例触发
update,执行Child.render()→ 生成新 Child VNode - 接着调用
patch(childOldVNode, childNewVNode),开始 Child 自身的 diff(比如比对其内部的<div>{{ msg }}</div>)

















