React 的渲染流程

107 阅读4分钟

React 的渲染流程是一个复杂但高效的过程,它的核心目标是将组件的状态和属性变化反映到用户界面上,同时尽可能优化性能。React 的渲染流程可以分为以下几个阶段:初始化渲染更新渲染组件卸载。其中,初始化和更新渲染流程是最核心的部分。


React 渲染流程:总体结构

  1. 初始化阶段

    • 初次渲染时,构建组件树并挂载到页面上。
  2. 更新阶段

    • 组件状态或属性变化时,根据新旧状态重新渲染需要更新的部分。
  3. 卸载阶段

    • 组件被移除时执行清理操作。

渲染流程细分为以下三个主要步骤:

阶段子步骤关键方法/概念
Render 阶段1. 构建或更新 Fiber 树beginWorkreconcileChildren
2. 调和新旧 Fiber 树,生成更新计划DiffingeffectTag
Commit 阶段3. 应用更新到真实 DOMcommitWorkcommitPlacement
4. 执行副作用(useEffect 等)commitEffectListMount
Cleanup 阶段5. 清理无效的副作用useEffect 清理函数

详细的渲染流程

1. Render 阶段:生成 Fiber 树(可中断)

Render 阶段的目标是计算出需要更新的内容,生成或更新 Fiber 树(React 内部的数据结构),并标记需要更新的部分。

步骤:

  1. 开始渲染(beginWork 方法)

    • React 从根节点开始调用 beginWork 方法,根据组件类型(类组件、函数组件、DOM 节点等)处理 Fiber 节点。
    • 如果组件有子组件,递归调用 beginWork,生成或更新子节点的 Fiber 树。
  2. 协调(Reconciliation)

    • 使用 Diffing 算法 比较新旧 Fiber 树,找出需要更新的部分。
    • 给需要更新的 Fiber 节点添加 effectTag,标记更新类型(如 UPDATEPLACEMENTDELETION)。
  3. 完成渲染(completeWork 方法)

    • React 自底向上调用 completeWork,将子组件的更新信息汇总到父组件。
    • 生成一个包含所有更新计划的「副作用列表」(Effect List)。

特点:

  • 可被中断:支持异步渲染,允许在高优先级任务(如用户输入)到来时暂停渲染并恢复。

2. Commit 阶段:应用更新到真实 DOM(不可中断)

Commit 阶段的目标是将 Render 阶段生成的更新计划应用到真实的 DOM,并执行副作用。这一阶段是同步的,不能被中断。

步骤:

  1. DOM 更新

    • 根据 Fiber 节点的 effectTag,执行对应的 DOM 操作:

      • PLACEMENT:插入新的节点到 DOM。
      • UPDATE:更新节点的属性。
      • DELETION:从 DOM 中移除节点。
  2. 执行生命周期方法

    • 调用类组件的生命周期方法(如 componentDidMountcomponentDidUpdate)。
    • 触发 useEffect 的副作用回调。
  3. 清理无效的副作用

    • 调用 useEffect 的返回清理函数,移除不再需要的副作用。

3. Cleanup 阶段:清理工作

当组件从页面中移除或状态更新时,React 会在适当时机清理无效的资源和副作用。

步骤:

  • 调用组件的 componentWillUnmount 方法(类组件)。
  • 执行 useEffect 返回的清理函数。
  • 释放与 Fiber 节点相关的内存资源。

React 渲染流程图

1. Render 阶段(可中断)
-------------------------------------------
  (1) 构建或更新 Fiber 树
  (2) 比较新旧 Fiber 树(Diffing)
  (3) 标记需要更新的节点(effectTag)
-------------------------------------------
2. Commit 阶段(不可中断)
-------------------------------------------
  (4) 应用更新计划到 DOM
  (5) 执行副作用(生命周期方法、useEffect)
-------------------------------------------
3. Cleanup 阶段
-------------------------------------------
  (6) 清理副作用和资源

React 渲染优化点

  1. 异步渲染和中断(React 18 并发特性)

    • Render 阶段可以被时间切片中断,以响应高优先级任务。
    • React 使用调度器控制任务优先级。
  2. Diffing 算法优化

    • 使用 key 标记列表中的唯一项,避免不必要的 DOM 重排。
    • 按需更新:只更新标记为 effectTag 的 Fiber 节点。
  3. Hooks 副作用清理

    • React 会确保在重新渲染或组件卸载时清理无效的副作用。
  4. React.memo 和 React.PureComponent

    • 减少不必要的重新渲染。

代码示例:渲染流程解析

function App() {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    console.log("Effect: Component rendered");
    return () => console.log("Cleanup: Component will re-render");
  }, [count]);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

流程解析

  1. Render 阶段

    • setCount 更新状态,触发组件重新渲染。
    • React 创建新 Fiber 树并比较新旧树。
    • 标记 <p> 节点的 effectTagUPDATE
  2. Commit 阶段

    • 更新 DOM,将新的 count 值反映到 <p> 节点。
    • 执行 useEffect 回调,输出 "Effect: Component rendered"
  3. Cleanup 阶段

    • 在下一次渲染之前,执行返回的清理函数,输出 "Cleanup: Component will re-render"

React 的渲染流程通过 Render 和 Commit 阶段分离,提高了性能和灵活性。对于开发者来说,理解这些阶段的职责有助于编写更高效和优化的代码。