react 的 diff 原理

React 的 diff(调和 reconciliation)原理是 React 高性能的核心关键,它决定了当 state / props 发生变化时,React 如何高效更新 DOM。

React 为什么需要 Diff?

原生 DOM 操作非常慢,如果每次状态变化就 重新生成整个 DOM,性能会非常差。

React 通过 虚拟 DOM(vDOM) 和 diff 算法:

  • 找出“变化的部分”

  • 只更新必要的 DOM 节点(最小代价)

  • 避免整棵 DOM 重建

React Diff 的三个核心策略(非常重要)

React 的 diff 不是通用树 diff(O(n³)),而是基于实际 UI 情况进行了优化,将复杂度降低到 O(n)。

策略 1:不同类型的节点,直接销毁重建

例如:

<div>...</div>
<span>...</span>

React 不会比较子节点,直接删除旧节点,创建新节点。

策略 2:同类型节点,进行“细粒度比较”

例如:两个 <div>

  • React 会比较它们的 props

  • 只更新变化的属性,如 className、style 等

  • 再递归比较子节点

策略 3:对子节点列表(数组)使用 key,提高 diff 性能

列表的核心是 key 决定元素是否是同一个节点。

例如,没有 key:

<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ul>

如果你把 B 移到开头:

B A C

没有 key 的话,React 可能会:

  • 认为 A→B、B→A

  • 产生大量不必要的 DOM 移动

但如果 key 明确:

<li key="A">A</li>
<li key="B">B</li>
<li key="C">C</li>

React 会:

  • 根据 key 判断哪些节点可以复用

  • 只移动必要的节点

  • 性能非常高

React diff 的 3 种情况(详细流程)

React Diff 分为三种情况处理 vDOM 树:

情况 1:节点类型变化

旧 vDOM:<div />
新 vDOM:<span />

操作:

  • 旧 DOM 节点全部卸载

  • 新 DOM 节点重新创建

情况 2:节点类型相同

旧 vDOM:<div class="a">...
新 vDOM:<div class="b">...

操作:

  • 比较 props → 更新变化属性

  • 继续 diff 子节点

情况 3:列表 diff(重点)

React 使用 “从左到右、逐个对比 + key 搜索” 的策略。

核心步骤:
  1. 旧节点按 key 建 hashMap(O(n))

  2. 新列表逐个查 key(O(1) 查找)

  3. 判断:

  • 是否能复用旧节点?

  • 是否需要移动位置?

  • 是否需要插入新节点?

  • 是否要删除旧节点?

key 的作用:
  • key 不变 → 认为是同一个节点,更新内容,不重建

  • key 变化 → 认为是不同节点

错误:使用 index 作为 key

会导致:

  • 节点复用错误

  • 动画错乱

  • state 绑定错误(比如 input 的选中状态乱掉)

  • 性能下降,因为大量 DOM 需要替换

React Fiber 与 Diff 的关系

React 16+ 的 diff 基于 Fiber 架构,但核心思想不变:

Fiber 将更新拆成更小的“任务单元”
支持中断、恢复(可响应优先级,比如界面不卡顿)
每个 Fiber 节点都存储:
  • 当前 vDOM

  • 更新后的 vDOM

  • DOM 操作标记(比如 Placement, Update, Deletion)

Diff 发生在 Fiber 的 beginWork 与 completeWork 阶段。

总结

React Diff 原理
React 使用的是 O(n) 的启发式 tree diff 算法:
  • 不同类型节点 → 直接重建

  • 同类型节点 → 比较 props + 继续 diff 子节点

  • 列表使用 key → 快速定位可复用节点,减少 DOM 操作

面试时的总结
  • React 不是通用 diff,时间复杂度 O(n³) → O(n)

  • 核心优化点:分层比较、类型判断、key 定位

  • key 是 diff 的灵魂,使用 index 会产生问题

  • Fiber 将 diff 任务进一步切分,带来可中断渲染

posted @ 2025-12-05 09:27  煜火  阅读(7)  评论(0)    收藏  举报