React 渲染机制详解:状态更新时如何精准重建虚拟 DOM 树

1次阅读

React 渲染机制详解:状态更新时如何精准重建虚拟 DOM 树

react 并不会在每次状态更新时重新创建整个组件树的虚拟 dom;它仅对受状态变更影响的组件及其子树执行重渲染,并通过高效的 diff 算法比对新旧虚拟 dom 节点,实现最小化 dom 操作。

react 并不会在每次状态更新时重新创建整个组件树的虚拟 dom;它仅对受状态变更影响的组件及其子树执行重渲染,并通过高效的 diff 算法比对新旧虚拟 dom 节点,实现最小化 dom 操作。

在 React 中,“重渲染(re-render)”常被误解为“整棵树重建”。实际上,React 的渲染行为高度精细化且具备明确的边界控制能力。当某个组件(例如组件 D)内部调用 setState 或触发 useState 更新时,React 仅对该组件及其子组件(即其子树)触发函数执行或 render 方法调用,而兄弟节点(如 C 的其他子组件)、父组件(如 C、B、A)默认不会重新执行。

✅ 正确的渲染路径示例

假设组件层级为:

function A() { return <B />; } function B() { return <C />; } function C() { return <div><D /><OtherSibling /></div>; } function D() {   const [count, setCount] = useState(0);   return <button onClick={() => setCount(c => c + 1)}>{count}</button>; }

当 D 内部 count 更新时:

  • ✅ D 函数被重新调用,生成新的 React Element(即虚拟 DOM 节点)
  • ✅ D 的直接子组件(如有)也会参与重渲染(深度优先)
  • ❌ C、B、A 不会重新执行(除非它们自身依赖了该 state,或被显式 memo 失效)
  • ❌ OtherSibling 完全不受影响,跳过渲染

? 技术本质:React 的协调器(Reconciler)以“fiber 节点”为单位构建工作单元。状态更新会标记对应 fiber 及其子 fiber 为 dirty,后续 render 阶段仅遍历这些 dirty 区域,而非全量遍历整棵树。

? Diffing 并非跨组件,而是基于 fiber 子树的局部比对

React 的 diff 算法(双端对比 + key 优化)作用范围是同一父节点下的子元素列表。例如在 D 返回的 JSX 中:

return (   <div>     <h2>Counter: {count}</h2>     <span className={count > 5 ? 'high' : 'normal'}>       {count}     </span>   </div> );

React 会将本次 D 生成的新 React.createElement 结果(即新的虚拟 DOM 片段),与其上一次输出进行逐层比对:

  • 标签类型未变 → 复用 DOM 节点,仅更新文本内容

  • 的 className 属性变化 → 标记 class 更新操作
  • 整个
    子节点结构未增删 → 无需移动或销毁节点

    ⚠️ 注意:diff 不跨层级比较(如不会把 D 的新输出和 OtherSibling 的旧输出做对比),它严格限定在当前组件返回的 JSX 结构内部。

    ? 关键保障机制:默认浅比较与可优化边界

    • 函数组件无 memo 默认全量重渲染子树:若 C 未包裹 React.memo,即使 D 更新,C 本身仍会重新执行(但通常只返回相同 JSX,diff 后无真实 DOM 变更)。
    • 使用 React.memo / useMemo / useCallback 可切断不必要的向下传播
      const D = React.memo(function D({ onIncrement }) {   // ... });

      此时若 C 传给 D 的 props 未变,即使 C 重渲染,D 也不会触发二次渲染。

    ✅ 总结:React 渲染的三大核心原则

    1. 自顶向下标记,自底向上提交:状态更新触发对应 fiber 标记为 dirty,reconcile 阶段自上而下遍历 dirty 区域,commit 阶段自下而上应用 DOM 变更;
    2. 局部 diff,非全局重建:虚拟 DOM 重建与 diff 均限于变更组件及其子树,不涉及无关分支;
    3. 不可变更新驱动确定性:每次渲染都生成全新 React Element 对象,确保 diff 算法有可靠的新旧参照,这是高效更新的前提。

    理解这一机制,不仅能写出更可控的性能代码,也是合理使用 memo、useMemo 和设计组件粒度的基础。

text=ZqhQzanResources