React 是目前前端最流行的框架之一,但很多开发者只会停留在使用层面,对于 React 内部是如何运作的缺乏深入理解。本文将带你从源码角度出发,梳理 React 的运行流程,帮助你建立整体认知。
一、源码结构概览
在深入分析之前,我们需要知道 React 源码大致分为以下几个部分:
- react :核心 API(
useState
、useEffect
等 hooks,以及createElement
等方法)。 - react-dom:负责 DOM 渲染相关逻辑,包括调和(reconciliation)、Fiber 架构实现等。
- scheduler:调度优先级,决定任务的执行顺序。
- shared:共享工具方法和常量。
了解目录结构有助于我们在阅读源码时快速定位。
二、React 启动流程
当我们在应用里写下:
javascript
import React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(<App />, document.getElementById("root"));
那么内部会经历怎样的流程呢?
1. ReactDOM.render
- 入口位于
react-dom
包。 - 会调用
legacyRenderSubtreeIntoContainer
→renderSubtreeIntoContainer
→ 最终进入createRoot
(React 18 以后则推荐使用createRoot
API)。
2. 创建 FiberRoot
- React 会为整个应用创建一个 FiberRoot,它是 Fiber 架构的根节点。
- 每个组件会对应一个 FiberNode。
3. 开始初次渲染
- 初次渲染走 同步模式,即直接构建 Fiber 树并渲染到 DOM。
- 过程中会执行组件函数,调用 Hooks,得到虚拟 DOM。
三、Fiber 架构与调和流程
1. Fiber 的作用
- 链表结构:每个 FiberNode 保存对父节点、子节点、兄弟节点的引用。
- 可中断渲染:React 将渲染拆解为任务片段,可以被打断和恢复。
2. Reconciliation(调和)
- 对比新旧 Fiber 树,找出需要更新的节点(Diff 算法)。
- 生成 effect list(副作用链表),记录需要对 DOM 做的操作。
3. Commit 阶段
分为三个小步骤:
- before mutation :在 DOM 变更前执行(比如
getSnapshotBeforeUpdate
)。 - mutation:执行 DOM 更新。
- layout :调用
componentDidMount
、useLayoutEffect
等。
四、Hooks 的执行原理
以 useState
为例:
- 初次渲染时,React 会在 FiberNode 上建立一个 hook 链表,每个 hook 保存状态。
useState(initialState)
会返回一个[state, dispatch]
。- 当调用
dispatch
时,会创建一个 update 对象,放入更新队列。 - 下次渲染时,从队列里取出最新状态,计算后替换。
这也是为什么 hook 必须写在函数组件最外层,不能在 if/else 里写 ------ React 需要保证 hook 顺序一致。
五、Scheduler 调度
React 并不是所有任务都立即执行,而是根据优先级:
- Immediate:立即执行(比如用户输入)。
- User-blocking:高优先级(比如按钮点击)。
- Normal:普通渲染任务。
- Low / Idle:低优先级任务。
Scheduler 通过 requestIdleCallback
、MessageChannel
等机制实现任务切片,让渲染更流畅。
六、整体流程图
php
ReactDOM.render
↓
创建 FiberRoot → 创建 Fiber 树
↓
Render 阶段(调和:Diff 算法,构建 Fiber 树)
↓
Commit 阶段(DOM 更新,调用生命周期/Effect)
↓
Scheduler 负责调度优先级
七、总结
React 源码解读的核心在于三个关键点:
- Fiber 架构:链表化数据结构,支持可中断渲染。
- 调和 & 提交:Diff 算法找出变化,commit 阶段更新 DOM。
- Scheduler 调度:任务分片与优先级控制,提升流畅度。
如果你想深入研究 React 源码,可以从 react-reconciler
和 scheduler
两个包入手,配合调试工具一步步跟踪调用链路。