Transition通过标记vnode、拦截patch、注入钩子三重机制干预diff:enter时添加类并延后挂载,leave时标记isLeaving并暂停子树diff,依赖insert/remove/update钩子与帧同步、状态同步、生命周期同步协同实现动画精准控制。

Diff 算法本身不感知动画,它只负责判断节点是否复用、移动、新增或销毁;但当节点被 Transition 包裹时,框架(如 Vue)会通过节点标记 + 钩子拦截 + patch 时机协同,让动画在正确阶段启动、等待和收尾。
Transition 如何干预 diff 流程?
Transition 不是真实 DOM 元素,而是一个逻辑包装器。它在 patch 前就监听 vnode 的“存在性变化”:
- 当新 vnode 出现、旧 vnode 为 null → 触发
enter阶段:自动添加v-enter类,并在下一帧切换为v-enter-active - 当新 vnode 为 null、旧 vnode 存在 → 触发
leave阶段:添加v-leave类,立刻切为v-leave-active,但阻止默认 remove 操作 - Transition 内部会把该 vnode 标记为
isLeaving: true,并暂停其父级的子节点 diff,直到动画完成
Patch 钩子如何与动画帧对齐?
关键在于 patch 过程中插入的底层 DOM 操作钩子,它们比组件生命周期更早、更精准:
-
insert(el):节点已挂载到真实 DOM 后立即执行 → 适合调用el.animate()或requestAnimationFrame启动入场动画 -
remove(el, done):节点即将被移除前触发 → 必须显式调用done()告知 patch “等我动画结束”,否则 patch 会跳过等待、直接删掉 DOM -
update(oldVnode, vnode):可用于属性过渡(如 opacity、transform),对比前后 style 差异后触发 CSS transition 或 JS 补间
为什么不能只靠 CSS 类名切换?
CSS 类名切换只是声明式入口,真正实现“动画完成才更新 DOM”依赖三重同步:
-
帧同步:Transition 内部用
nextTick + getComputedStyle强制触发重排,确保类名变更在下一帧生效 -
状态同步:patch 过程中,vnode 的
data.transition字段会被注入钩子函数,与 Transition 实例绑定 -
生命周期同步:
leave动画期间,vnode 仍保留在虚拟树中,diff 会跳过对其子树的递归 patch,避免中间态错乱

















