虚拟DOM的Diff算法以确定性、可预测性和高效复用为目标,通过同层比较和key驱动节点识别确保行为一致;缺失或不稳定的key会导致顺序匹配退化,引发UI错乱与状态丢失。

虚拟 DOM 的 diff 算法本身不追求“绝对稳定”,而是以**确定性、可预测性和高效复用**为设计目标。它的“稳定性”体现在行为一致、更新可控,而非状态或结构永不变化。
稳定性核心:同层比较与 key 驱动的节点识别
diff 算法默认只在相同层级内逐一对比节点,不跨级查找或移动。这意味着只要父子关系未变,节点的比较范围就被严格限定——这是行为可预期的基础。
- 同一位置节点类型不同(如 div → span),直接替换整棵子树,不尝试复用;
- 同一位置节点类型相同但 props 或 children 变化,则仅更新差异属性或递归 diff 子节点;
- 列表中每个子节点若带 唯一且稳定 的 key,算法就能准确识别“哪个节点被移动、新增或删除”,避免误判导致的 UI 错乱或状态丢失。
key 缺失或不稳是稳定性的最大风险点
当列表项缺少 key,或 key 动态生成(如用 index)、重复、或随数据变化而改变时,diff 将退化为顺序匹配:
- 插入新项在开头,后续所有节点因索引偏移被当作“变更”重新渲染;
- 带输入框的列表项可能丢失焦点或内容,因为旧节点未被复用;
- 组件内部状态(如表单值、动画进度)容易意外重置。
这不是算法缺陷,而是 key 语义未被正确表达导致的逻辑不稳定。
框架差异不影响稳定性本质,只影响优化边界
React 采用自顶向下深度优先 + key 映射;Vue 使用双端对比(头/尾指针)+ 最长递增子序列优化移动。两者都坚守“同层不可跨”“key 决定身份”的原则:
- 它们不会因运行环境、时间或调用次数不同而产生歧义结果;
- 给定相同的前后两棵虚拟 DOM 树,diff 输出的 DOM 操作序列始终一致;
- 所谓“不稳定”现象,几乎全部源于开发者未提供稳定 key、或在 render 中非纯地生成 vnode(如每次返回新对象引用)。
提升稳定性的实践要点
真正决定 diff 行为是否可靠的是代码写法,而非算法本身:
- 列表项必须使用业务上持久唯一的 id 作为 key(禁用 index);
- 避免在 render 中创建新函数、新对象或新数组(防止 vnode 引用变化触发无谓更新);
- 对有内部状态的组件,确保其 props 是可比较的,必要时用 useMemo / memo 包裹;
- 理解“节点身份”由 tag + key 共同定义——改 tag 或换 key 都意味着销毁重建。


















