- 教程
- 前端
【免费下载链接】react-illustration-series
图解react源码, 用大量配图的方式, 致力于将react原理表述清楚.
本文以 react-illustration-series 的系列总览文档(docs/index.md)为骨架,结合 readme.md 与
docs/main、docs/algorithm、docs/interview下各篇原理文章,为读者梳理一条"从宏观包结构到微观运行机制"的 React 17 源码阅读路径。读完本文,你将掌握该仓库的内容版图、核心包职责划分、两大工作循环的主干逻辑、三种启动模式,以及如何在本机以 dumi 站点形式浏览全文。
系列定位:用配图讲清 React 原理
react-illustration-series 是一个以"图解"为核心形式的 React 源码原理系列,仓库描述明确写道:"图解 react 源码,用大量配图的方式,致力于将 react 原理表述清楚。"整个系列基于react@17.0.2展开分析,并尽可能跟随 React 版本升级持续更新;随着官方升级,主要版本的文章会保存在以版本号命名的分支中(当前仓库主分支对应 v17.x 系列,历史版本另有 v16.13.1、v17.0.1 分支)。
系列的展开主线索是react 核心包结构和运行机制,覆盖的核心内容板块包括:
- react 宏观结构(核心包之间的依赖与调用关系)
- react 工作循环(任务调度循环与 fiber 构造循环)
- react 启动模式(legacy / Blocking / Concurrent)
- react fiber 原理(树构造、对比更新、渲染提交)
- react hook 原理(状态 Hook、副作用 Hook)
- react 合成事件原理
仓库本身是一份以 dumi 驱动的静态文档站点(见 package.json,依赖dumi@^2.2.0),支持npm run dev在本地启动文档站逐篇阅读。
适用读者
官方在 docs/index.md 中给出了两条读者门槛,定位相当明确:
- 对
react、react-dom开发 web 应用有实践经验; - 期望深入理解
react内在作用原理。
也就是说,这个系列不是入门教程,适合已经能熟练使用 React 写应用、想进一步弄清"框架内部到底怎么运行"的开发者。系列同时强调"不是面经,但会列举一些面试题来加深对 react 理解"——面试题板块独立存在于 docs/interview/index.md,后续会详细介绍。
内容版图:五大板块构成完整学习路径
综合 readme.md 与 docs/index.md 的目录组织,整个系列的内容可划分为五个板块。以下逐一给出对应文章与主题要点,所有路径均以仓库根目录为基准。
1. 基本概念(先建立宏观认知)
- 宏观包结构:介绍 web 开发相关的 4 个核心包及架构分层;
- 两大工作循环:
任务调度循环与fiber构造循环的分工与联系; - 高频对象:梳理 fiber、updateQueue、task 等贯穿始终的数据对象。
2. 运行核心(主线:一次更新如何变成一次渲染)
- reconciler 运作流程:把 reconciler 归结为"输入 → 注册调度任务 → 执行调度任务 → 输出"等步骤;
- 启动过程:三种启动模式及三个全局对象的创建;
- 优先级管理:React 体系中的优先级分类与转换;
- scheduler 调度原理:任务队列、时间切片与回调时机控制;
- fiber 树构造(基础准备)、fiber 树构造(初次创建)、fiber 树构造(对比更新):fiber 树三阶段构造细节;
- fiber 树渲染:commit 阶段的副作用处理与 DOM 输出;
- 异常处理(系列规划中的主题)。
3. 数据管理(状态、副作用与跨组件数据)
- 状态与副作用:class / function 组件中改变 fiber 节点状态与副作用的 api;
- hook 原理(概览):Hook 与 Fiber 的关系及数据结构;
- hook 原理(状态 Hook)、hook 原理(副作用 Hook);
- context 原理。
4. 交互(事件体系)
- 合成事件原理:事件代理与两阶段派发机制。
5. 高频算法(阅读源码前的前置功课)
- 位运算:fiber 的 flags、lane 等大量基于位运算的标记设计;
- 堆排序:任务调度循环所使用的二叉堆;
- 深度优先遍历:fiber 树构造的基本遍历方式;
- 链表操作:fiber 链式结构、更新队列的合并;
- 栈操作:上下文栈的入栈出栈;
- diff 算法:reconcile 子节点的核心算法。
这五个板块在阅读顺序上存在依赖关系:建议先读"基本概念"建立全局观,再进入"运行核心"跟完整条渲染链路,随后用"数据管理"与"交互"补全组件层细节,过程中按需查阅"高频算法"作为理解工具。
核心包结构:四个包如何分工协作
宏观包结构一文指出,React 工程目录 packages 下共有 35 个包(对应@17.0.2版本),其中与web 开发相关的核心包只有 4 个,整个系列近 20 篇文章正是以这 4 个包为线索展开:
| 包 | 职责定位 | 开发者在日常编码中的接触面 |
|---|---|---|
react | 基础包,只提供定义 React 组件(ReactElement)的必要函数 | 编写应用时调用的绝大多数 api 均来自此包 |
react-dom | React 与 web 平台连接的桥梁(浏览器与 Node.js 环境),把 reconciler 的运行结果输出到界面上 | 多数场景只用到入口函数ReactDOM.render(<App/>, document.getElementById('root')) |
react-reconciler | React 得以运行的核心包,综合协调react-dom、react、scheduler各包之间的调用与配合 | 通常不会直接使用 |
scheduler | 调度机制的核心实现,控制回调函数的执行时机,concurrent 模式下可实现任务分片 | 通常不会直接使用 |
其中react-reconciler与scheduler的协作链路可以这样概括:react-reconciler接受输入(scheduleUpdateOnFiber),把 fiber 树生成逻辑封装进回调函数(涉及 fiber 树形结构、fiber.updateQueue队列、调和算法等),再将回调(performSyncWorkOnRoot或performConcurrentWorkOnRoot)送入scheduler调度;scheduler控制回调执行时机,回调执行完毕后得到全新的 fiber 树;最后调用渲染器(react-dom、react-native等)把 fiber 树结构反映到界面上。
为了便于理解,该文还提出了一套**接口层(api)/ 内核层(core)**的二分视角(并注明这并非官方术语,仅为理解 react 而做的分解):
- 接口层:
react包。React 启动后能改变渲染的基本操作只有 3 种——class 组件中调用setState()、function 组件中通过 hook 发起dispatchAction改变 hook 对象、改变 context(也需要setState或dispatchAction辅助)。这些 api 均由react包直接暴露。 - 内核层:由 3 部分构成——
- 调度器(
scheduler包):唯一职责是执行回调。把react-reconciler提供的回调包装成任务对象,内部维护一个按优先级排列的任务队列(二叉堆),循环消费直到队列清空; - 构造器(
react-reconciler包):装载渲染器(渲染器必须实现HostConfig协议,如react-dom);接收react-dom初次 render 与react包后续 setState 发起的更新请求;将 fiber 树构造过程包装成回调交给scheduler等待调度; - 渲染器(
react-dom包):引导应用启动(ReactDOM.render),并实现HostConfig协议,把 fiber 树表现为真实 dom 节点(浏览器中)或字符串(SSR 场景)。
- 调度器(
上图即 宏观包结构 一文中的内核关系概览:红色方块代表入口函数,绿色方块代表出口函数,包之间的调用脉络正是通过板块间的入口、出口函数连接起来的。理解了这张图,就基本掌握了 react 内核层的宏观结构,后续各章节均按此图的思路逐模块深入。
两大工作循环:React 运行的主干逻辑
两大工作循环 从概览图中提炼出 React 运行的两个大循环:
- 任务调度循环:源码位于
scheduler包的Scheduler.js,是 react 应用得以运行的保证,负责循环调用、控制所有任务(task)的调度。它以二叉堆为数据结构(详见堆排序),循环执行堆顶任务直到堆被清空;逻辑偏向宏观,只调度 task,不关心任务具体内容(甚至可以把Scheduler包脱离 react 单独使用),具体任务就是执行回调performSyncWorkOnRoot或performConcurrentWorkOnRoot。 - fiber 构造循环:源码位于
react-reconciler包的ReactFiberWorkLoop.js,控制 fiber 树的构造,整个过程是一个深度优先遍历(详见深度优先遍历);它以树为数据结构,逻辑偏向具体实现,只是 task 的一部分(如performSyncWorkOnRoot包含 fiber 树构造、DOM 渲染、调度检测),只负责 fiber 树构造。
两者的关系是从属关系:fiber 构造循环是任务调度循环中 task 的一部分,每个 task 都会重新构造一棵 fiber 树。两大循环的分工可总结为:大循环(任务调度循环)负责调度 task,小循环(fiber 构造循环)负责实现 task。
由此,React 运行的主干逻辑——即"输入转换为输出"的核心步骤——可概括为三步:
- 输入:把每一次更新(新增、删除、修改节点)视为一次"更新需求"(目的是更新 DOM 节点);
- 注册调度任务:
react-reconciler收到更新需求后并不立即构造 fiber 树,而是去scheduler注册一个新 task,即把更新需求转换成一个 task; - 执行调度任务(输出):
scheduler通过任务调度循环执行 task(执行过程又回到react-reconciler包中)。其中fiber构造循环是 task 的实现环节之一,循环完成后构造出最新 fiber 树;commitRoot是 task 的实现环节之二,把最新 fiber 树渲染到页面上,task 完成。
为了获得更好的性能(如批量更新、可中断渲染),React 在这条"输入 → 输出"链路上做了大量优化,而"任务调度循环 + fiber 构造循环"的相互配合正是实现可中断渲染的基础。
三种启动模式与三个全局对象
启动过程 一文衔接 reconciler 运作流程的"输入"步骤,讲解应用的启动过程。在当前稳定版react@17.0.2源码中存在 3 种启动方式:
| 模式 | 入口 api | 代码形式 | 特性 |
|---|---|---|---|
legacy模式 | ReactDOM.render(<App />, rootNode) | ReactDOM.render(<App />, document.getElementById('root'), (dom) => {});(支持回调,参数是 dom 对象) | 当前 React app 实际使用的方式,可能不支持 concurrent 的新功能 |
Blocking模式 | ReactDOM.createBlockingRoot(rootNode).render(<App />) | 先createBlockingRoot(document.getElementById('root'))创建 ReactDOMRoot 对象,再调用.render(<App />)(不支持回调) | 实验中,仅提供 concurrent 模式的小部分功能,作为迁移第一步 |
Concurrent模式 | ReactDOM.createRoot(rootNode).render(<App />) | 先createRoot(document.getElementById('root'))再.render(<App />)(不支持回调) | 实验中,开启所有新功能,未来计划作为 React 默认开发模式 |
需要特别说明的是:虽然17.0.2源码中存在createRoot与createBlockingRoot方法(自行构建时默认构建 experimental 版本),但稳定版的构建入口(packages/react-dom/index.stable.js)排除了这两个 api,所以npm i react-dom安装的17.0.2稳定版中不能使用这两个 api。要体验非 legacy 模式,需要显式安装 alpha 版本或自行构建。
无论哪种模式,React 初始化时都会创建 3 个全局对象(一经创建大多数场景不会再销毁,除非root.unmount()卸载整个应用):
ReactDOM(Blocking)Root对象:属于react-dom包,暴露render、unmount方法,通过调用其render方法引导应用启动;fiberRoot对象:属于react-reconciler包,作为 reconciler 运行过程中的全局上下文,保存 fiber 构建过程中依赖的全局状态,其大部分实例变量用于存储 fiber 构造循环过程中的各种状态,react 应用内部据此控制执行逻辑;HostRootFiber对象:属于react-reconciler包,是 React 应用中第一个 Fiber 对象、Fiber 树的根节点,节点类型为HostRoot。
三个对象创建过程中的RootTag参数在三种模式下各不相同,该类型决定了整个 react 应用是否支持可中断渲染。
scheduler 调度:时间切片与任务队列
调度原理 将调度中心(scheduler包)称为"React 运行时的中枢"。其最核心的代码在SchedulerHostConfig.default.js,共导出 8 个函数,核心逻辑集中在其中:
export let requestHostCallback; // 请求及时回调: port.postMessage export let cancelHostCallback; // 取消及时回调: scheduledHostCallback = null export let requestHostTimeout; // 请求延时回调: setTimeout export let cancelHostTimeout; // 取消延时回调: cancelTimeout export let shouldYieldToHost; // 是否让出主线程(currentTime >= deadline && needsPaint) export let requestPaint; // 请求绘制: 设置 needsPaint = true export let getCurrentTime; // 获取当前时间 export let forceFrameRate; // 强制设置 yieldInterval, 从源码引用看几乎没有用到由于 react 可在 Node.js 环境使用,这些函数在不同 JS 执行环境中实现会有差异。基于普通浏览器环境,其关键机制如下:
- 异步回调:
requestHostCallback保存回调后通过MessageChannel的port.postMessage(null)触发performWorkUntilDeadline,最终执行scheduledHostCallback。注意MessageChannel在浏览器事件循环中属于宏任务,所以调度中心永远是异步执行回调函数; - 时间切片:默认
yieldInterval为 5ms,如果一个 task 运行超过该周期,下一个 task 执行之前会把控制权归还浏览器,让浏览器处理用户输入、UI 绘制等紧急任务(shouldYieldToHost判断currentTime >= deadline && needsPaint)。
Hook 原理与数据管理概览
在"数据管理"板块,hook 原理(概览) 指出:使用任意 Hook api,最终都是为了控制 fiber 节点上"状态相关"与"副作用(Effect)相关"的属性——状态相关包括pendingProps、memoizedProps、updateQueue、memoizedState,副作用相关包括flags、nextEffect、firstEffect、lastEffect。Hook 的数据结构定义于ReactFiberHooks.old.js,核心为:
export type Hook = {| memoizedState: any, // 当前状态 baseState: any, // 基状态 baseQueue: Update<any, any> | null, // 基队列 queue: UpdateQueue<any, any> | null, // 更新队列 next: Hook | null, // next指针 |};连同 状态与副作用、hook 原理(状态 Hook)、hook 原理(副作用 Hook)、context 原理 四篇,共同构成理解 React 数据流在 fiber 层面的完整拼图。官方也解释了引入 Hook 的动机——组件间复用状态逻辑很难、复杂组件难以理解、class 难以理解;同时 Hook 在设计上还避免了 class 需要的额外开支(类实例创建、构造函数中绑定事件处理器等),组件树层次也更浅。
面试题板块:以题带点巩固理解
系列单独设有面试题板块(docs/interview/index.md),题目会不断扩充,每题对应一个 issue 可参与讨论,当前已列出 11 题,例如:
- setState 是同步还是异步?
- class 组件生命周期有哪些?
- 重复调用 setState 会发生什么?
- 什么是 fiber 架构?
- 调和算法具体干什么的?
- key 有什么作用,可以省略吗?
- useState() 如何实现数据持久化?
- useEffect() 会有内存泄漏吗?
useEffect(function, deps)中第二个参数表示依赖项,如何实现依赖项的对比?- 什么是可中断渲染?
- context 有什么作用?
这些题目与上述各篇原理文章一一呼应,可用于读完对应章节后的自测与复盘。
版本策略与变更要点
系列最初写作于 2020 年 6 月(当时稳定版为 v16.13.1),随着 React 官方升级,主要版本文章保存在以版本号命名的分支中。主分支当前基于react@17.0.2,历史版本分支包括 v16.13.1 与 v17.0.1。readme.md 记录了 v17 系列的版本跟踪要点:
- v17.0.0(主版本升级):使用层面基本维持不变,源码层面有两大重大变动——重构
Fiber.expirationTime并引入Fiber.lanes(react-reconciler板块);事件代理节点从 document 变成 rootNode、取消合成事件的缓存池(legacy-events被移除,改为react-dom/events)。 - v17.0.1:相比 v17.0.0 只改动了 1 个文件,修复 ie11 兼容问题,同时提升 v8 内部执行性能。
- v17.0.2:改动集中于
Scheduler包,主干逻辑没有变动,仅涉及调度性能统计相关。
系列暂时只跟踪稳定版本变动;由于 react 仓库代码改动频繁,写作过程中伴随小版本发布时,文章中的源码链接以写作当天的最新小版本为基准。
本地阅读与运行方式
仓库是一份基于 dumi 的静态站点工程(package.json),克隆到本地后即可把整个系列作为文档站浏览:
git clone https://gitcode.com/gh_mirrors/re/react-illustration-series.git cd react-illustration-series npm install npm run dev # 启动 dumi 开发服务器 npm run build # 构建静态站点也可直接按 Markdown 文件路径在仓库中阅读,例如从 docs/main/macro-structure.md 开始,按"基本概念 → 运行核心 → 数据管理 → 交互 → 高频算法"的顺序逐步深入。
总结
react-illustration-series 的核心价值在于:以react 核心包结构和运行机制为主线索,用大量配图把"输入更新 → 注册调度任务 → 执行回调 → 构造 fiber 树 → commit 渲染"这条主干链路讲透。建议读者先通读宏观包结构与两大工作循环建立整体认知,再结合本文梳理的内容版图逐篇深入,过程中用高频算法系列补齐数据结构基础,用面试题板块检验理解深度。正如系列使用指南所言:"真正对于技术本身,不能急于求成,需要静下心来修炼"——这份仓库正是为此准备的一份结构化、可循序渐进的学习材料。
- 教程
- 前端
【免费下载链接】react-illustration-series
图解react源码, 用大量配图的方式, 致力于将react原理表述清楚.
相关推荐
Nango 仓库 agent-builder-skill 深度解析:五款生产级 Claude Code Subagent 完整示例与落地指南
Nango 仓库 agent builder skill 深度解析:五款生产级 Claude Code Subagent 完整示例与落地指南 导读 本文以 Na
教程前端React源码中的队列操作:react-illustration-series之UpdateQueue入队与出队
React源码中的队列操作:react illustration series之UpdateQueue入队与出队 在React应用开发中,状态更新是核心流程之一
教程前端Faster Mask RCNN训练全攻略:从COCO数据集准备到1P/8P性能调优实战
Faster Mask RCNN训练全攻略:从COCO数据集准备到1P/8P性能调优实战 Faster Mask RCNN是基于PyTorch的高效目标检测与实
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考