React 如何组织组件树、暂停渲染,并把更新提交到页面?
先记住一句话:Fiber 既是 React 的内部数据结构,也是协调工作的基本单元。它让 React 能够保存渲染进度、标记更新优先级,并为可中断 Render 和并发特性提供基础。
Fiber 不等于线程,也不意味着所有 React 更新都会异步执行。它解决的核心问题是:把原本难以暂停的递归协调工作,拆成可以调度的工作单元。
React 15 主要使用 Stack Reconciler。开始一次更新后,JavaScript 调用栈会持续递归遍历组件树,直到这次工作完成。组件树较大或单个更新计算量较高时,主线程可能长时间无法处理用户输入和绘制。
Fiber 将组件树表示成一组 Fiber 节点,每个节点保存自己的工作状态和与相邻节点的关系。这样 React 可以:
一个 Fiber 可以简化理解成:
其中 return、child 和 sibling 让 React 能用类似链表的方式遍历树,而不必完全依赖 JavaScript 递归调用栈。
memoizedState 在函数组件中还会指向 Hook 链表,因此 useState、useEffect 等 Hook 能和当前组件 Fiber 关联起来。
React 通常维护两棵相关的 Fiber 树:
更新发生时,React 会基于 current 树构建或复用 workInProgress 树,并在其中完成 Render。Render 成功后,React 在 Commit 阶段把 workInProgress 切换为新的 current。
这种方式也常被称为双缓存:新的结果还没完成前,用户继续看到旧的、完整的 UI;未完成的 workInProgress 不会直接污染当前页面。
Render 阶段会:
Render 阶段可能被中断、重做或放弃,因此必须保持纯粹。不要在组件函数、useMemo 或其他渲染计算中执行请求、订阅、写 DOM 等副作用。
Commit 阶段会把已经确认的结果应用到宿主环境,通常要求快速、连续地完成,以避免页面处于半更新状态。它可以进一步理解为:
useLayoutEffect 等布局相关工作;useEffect 等 passive effect。useEffect 并不是在 Render 阶段执行的;Render 可能被放弃,而已执行的副作用无法被 React 自动撤销。
Fiber 和 Scheduler 让 React 能够将工作拆分并安排优先级,但 React 的 JavaScript 逻辑仍运行在浏览器主线程中。所谓“让出主线程”,是 React 在合适的边界暂停计算,让浏览器处理输入、绘制或其他任务,之后再继续。
因此:
startTransition 是优先级标记,不会把计算放进 Web Worker;React 18 的 startTransition、useDeferredValue 等公开 API 建立在这些能力之上,详见React 18 并发渲染。
Fiber 本身不等同于 Diff 算法,但它为协调过程提供了可保存的工作单元。协调时 React 会结合:
key;判断节点是复用、创建、移动还是删除。列表 key 的具体规则见Virtual DOM 和 Diff 算法。
组件可能在 Render 阶段执行多次,因此不要写出依赖“组件函数只执行一次”的代码:
应把外部系统同步放到事件处理函数或 effect 中,并提供清理逻辑:
Render 阶段生成的 workInProgress 可能被放弃。只有 Commit 后的 UI 才是用户真正看到的结果,只有 effect 或事件流程才适合与外部系统建立同步关系。
类组件中的以下生命周期可能在旧协调器中表现为一次调用,但在现代 React 的 Render 阶段可能被重复调用,因此被标记为不安全:
componentWillMount / UNSAFE_componentWillMountcomponentWillReceiveProps / UNSAFE_componentWillReceivePropscomponentWillUpdate / UNSAFE_componentWillUpdate它们不是简单地全部替换成 useEffect:
getDerivedStateFromProps;useEffect;useLayoutEffect;| 概念 | 主要职责 | 是否可能被中断 |
|---|---|---|
| Fiber | 保存节点信息和协调工作状态 | 工作单元可被重新安排 |
| Render | 计算下一棵树和变化标记 | 可以 |
| Commit | 把变化提交到 DOM 等宿主环境 | 通常需要连续完成 |
useLayoutEffect | DOM 更新后、绘制前同步处理 | 不属于可中断 Render |
useEffect | 提交后与外部系统同步 | 由 React 在提交后调度 |
可以这样回答:
Fiber 是 React 16 引入的协调架构和数据结构。它把组件树拆成可保存状态的 Fiber 节点,并通过 current / workInProgress 双树支持可中断的 Render。Render 阶段负责执行组件、协调新旧树,可以被暂停或重做;Commit 阶段负责把最终结果同步提交到 DOM。Fiber 不是线程,React 的副作用不能放在可能重复执行的 Render 阶段。
记住四个关键词:工作单元、双缓存、Render/Commit、渲染纯粹性。