学习 React 源码时,直接通读全部代码容易陷入细节、失去方向。React 源码的核心逻辑围绕「组件渲染与更新」展开,因此建议从 5个核心模块 切入,按「基础原理→核心流程→关键特性」的顺序递进,逐步建立完整的认知框架。以下是每个模块的核心内容、学习重点及理解技巧:

一、先搞懂:React 源码的「基础架构」(前置认知)

在深入具体模块前,需要先明确 React 的整体分层设计——这是理解后续模块协作的前提。React 源码本质是「分层职责清晰的协作系统」,核心分为3层:

分层 核心职责 关键模块/文件 为什么先学?
Scheduler 「任务调度」:决定什么时候执行什么任务 scheduler/ 目录 所有渲染、更新任务的“入口调度器”
Reconciler 「协调器」:找差异(Diff 算法)、标记更新 react-reconciler/ 目录 连接“调度”与“渲染”的核心逻辑
Renderer 「渲染器」:将差异渲染到真实平台(DOM/原生) react-dom/react-native/ 不同平台的“最终执行层”,DOM 场景最常用

学习重点:记住「调度→协调→渲染」的核心流程,理解各层的边界(比如 Scheduler 不关心“渲染什么”,只关心“什么时候执行”;Renderer 不关心“差异怎么来”,只关心“怎么渲染差异”)。

二、核心模块1:Scheduler(任务调度)—— React 的“大脑”

React 不是“同步执行所有更新”,而是通过 Scheduler 实现「优先级调度」(比如:用户输入的优先级 > 列表渲染的优先级),避免卡顿。这是 React 16+ 引入 Fiber 架构的核心支撑。

1. 核心职责
  • 管理「任务优先级」:定义 5 级优先级(如 ImmediatePriority 立即执行,IdlePriority 空闲时执行);
  • 调度「任务执行时机」:利用浏览器的 requestIdleCallback(降级用 setTimeout),在浏览器空闲时执行低优先级任务;
  • 中断与恢复:支持任务执行到一半被高优先级任务打断,后续再恢复执行(Fiber 架构的基础)。
2. 关键代码与概念
  • 任务队列:用「小顶堆」存储任务(按优先级排序,每次取优先级最高的任务);
  • unstable_scheduleCallback:调度任务的入口函数,接收“优先级”和“任务函数”,返回任务 ID;
  • unstable_cancelCallback:取消已调度但未执行的任务。
3. 学习技巧

不用纠结堆的实现细节,重点理解「为什么需要调度」:如果同步执行大量更新(比如渲染1000个列表项),会阻塞浏览器的 UI 线程,导致用户点击、输入无响应。Scheduler 通过“切片执行”(把长任务拆成多个短任务)解决这个问题。

三、核心模块2:Reconciler(协调器)—— React 的“差异计算器”

Reconciler 的核心是「Fiber 架构」和「Diff 算法」:它接收 Scheduler 调度的任务,遍历组件树找到“需要更新的部分”,并标记更新类型(如新增、删除、修改),最后把标记交给 Renderer 渲染。

1. 核心职责
  • 构建/更新「Fiber 树」:用 Fiber 节点替代传统的虚拟 DOM 树,支持任务中断与恢复;
  • 执行「Diff 算法」:对比新旧 Fiber 节点,计算差异(比如组件 props 变了吗?子节点顺序变了吗?);
  • 标记「更新优先级」:给需要更新的 Fiber 节点打上“优先级标签”(如 UpdatePriority),决定渲染顺序。
2. 关键代码与概念
  • Fiber 节点结构:每个组件对应一个 Fiber 节点,核心属性包括:
    • type:组件类型(如 divFunctionComponent);
    • stateNode:真实 DOM 节点(或类组件实例);
    • child/sibling/return:构建 Fiber 树的链表指针(替代虚拟 DOM 的递归树);
    • pendingProps/memoizedProps:新/旧 props,用于 Diff 对比;
    • effectTag:更新标记(如 Placement 新增、Update 修改、Deletion 删除)。
  • 「双 Fiber 树」机制
    • current 树:当前页面渲染的 Fiber 树(“旧树”);
    • workInProgress 树:正在构建的 Fiber 树(“新树”);
    • 构建完 workInProgress 树后,通过「树切换」(current = workInProgress)完成更新,避免页面闪烁。
  • Diff 算法规则(重点理解“同级对比”):
    • 只对比「同层级」节点,不跨层级对比(减少计算量);
    • 先按「key + type」匹配节点,匹配成功则复用,失败则删除旧节点、创建新节点;
    • 列表渲染必须加 key,否则会导致节点错误复用(比如删除列表第一项时,后面的项被错误更新)。
3. 学习技巧
  • 画一张 Fiber 树的结构图(用 child/sibling/return 连接节点),理解“链表遍历”如何替代“递归遍历”(递归无法中断,链表可以);
  • 用简单案例测试 Diff 逻辑(比如给列表添加/删除一项),观察 effectTag 的变化。

四、核心模块3:Renderer(渲染器)—— React 的“执行者”

Reconciler 只负责“找差异、打标记”,真正把差异渲染到页面的是 Renderer(比如 react-dom 负责浏览器 DOM,react-native 负责原生组件)。我们以最常用的 react-dom 为例。

1. 核心职责
  • 「提交更新」:接收 Reconciler 标记的 effectTag,执行真实 DOM 操作(如 createElementappendChildremoveChild);
  • 「处理副作用」:执行组件的 useEffectuseLayoutEffect 等副作用(useLayoutEffect 在 DOM 更新后同步执行,useEffect 在浏览器绘制后异步执行);
  • 「事件处理」:统一管理 React 合成事件(比如 onClick),解决浏览器事件兼容问题。
2. 关键代码与概念
  • commitRoot 函数:渲染的“提交入口”,分3个阶段执行:
    1. commitBeforeMutationEffects:执行 useLayoutEffect 的“销毁函数”(更新前);
    2. commitMutationEffects:执行 DOM 操作(新增/删除/修改节点);
    3. commitLayoutEffects:执行 useLayoutEffect 的“回调函数”,并更新 current 树。
  • 合成事件系统
    • React 不是把事件绑定在具体 DOM 节点上,而是绑定在 document 上(事件委托);
    • 通过 SyntheticEvent 封装原生事件,实现跨浏览器兼容(比如 e.preventDefault() 在各浏览器行为一致)。
3. 学习技巧
  • 打断点调试 commitRoot 流程:在 React 源码中找到 commitRoot 函数,在3个阶段分别加断点,观察 DOM 变化和副作用执行顺序;
  • 区分 useEffectuseLayoutEffect:前者在“浏览器绘制后”执行(不会阻塞渲染),后者在“DOM 更新后、绘制前”执行(会阻塞渲染,适合需要操作 DOM 后立即获取布局的场景)。

五、核心模块4:Hooks 系统—— 函数组件的“状态管理器”

React 16.8 引入 Hooks 后,函数组件可以拥有状态和副作用。Hooks 系统的核心是「如何在函数组件的每次渲染中,正确保存/读取状态、副作用等信息」。

1. 核心职责
  • 管理 Hooks 数据:将 useState/useEffect 等 Hooks 的数据存储在 Fiber 节点上;
  • 保证 Hooks 顺序:通过“链表”存储 Hooks,要求组件内 Hooks 不能在条件语句中使用(否则顺序会乱);
  • 触发 Hooks 更新:比如 setState 时,标记对应的 Fiber 节点为“待更新”,触发重新渲染。
2. 关键代码与概念
  • Hooks 存储位置:每个函数组件的 Fiber 节点上,有 memoizedState 属性,用于存储 Hooks 链表(每个 Hook 是链表的一个节点);
  • Hook 节点结构(以 useState 为例):
    const hook = {
      memoizedState: 0, // 当前状态值(如 useState(0) 的 0)
      baseState: 0,     // 基础状态(处理并发更新时用)
      queue: {          // 更新队列(存储 setXXX 触发的更新)
        pending: null   // 待执行的更新链表
      },
      next: null        // 下一个 Hook 节点
    };
    
  • useState 原理
    1. 首次渲染:创建 Hook 节点,存入 Fiber 的 memoizedState,返回 [state, setState]
    2. 后续渲染:从 Fiber 中读取已有的 Hook 节点,返回最新的 [state, setState]
    3. setState 触发:将更新任务加入 Hook 的 queue,并调度 Reconciler 执行更新。
3. 学习技巧
  • 理解“为什么 Hooks 不能在条件语句中使用”:比如 if (flag) useState(1),如果 flagtrue 变为 false,后续渲染时 Hooks 链表的顺序会错乱,导致读取到错误的状态;
  • 手动模拟 useState:用一个变量存储状态,一个函数修改状态并触发重新执行,理解其本质是“状态的持久化存储”。

六、核心模块5:Concurrent Mode(并发模式)—— React 的“高级特性”

Concurrent Mode 是 React 18 引入的核心特性,允许 React “同时处理多个更新”(比如一边渲染列表,一边响应用户输入),本质是 Scheduler + Reconciler 协作的高级体现。

1. 核心职责
  • 支持「可中断的渲染」:低优先级任务(如列表渲染)执行到一半时,若有高优先级任务(如用户点击),可以暂停低优先级任务,先执行高优先级任务;
  • 支持「 Suspense 组件」:等待异步数据加载时(如 fetchReact.lazy),显示 fallback 内容,数据加载完成后再渲染组件;
  • 支持「 Transitions 过渡更新」:标记某些更新为“过渡更新”(如 startTransition),优先级低于用户交互,避免卡顿。
2. 关键代码与概念
  • useTransition Hook:区分“紧急更新”(如输入框内容)和“过渡更新”(如根据输入筛选列表),过渡更新可被中断;
  • Suspense 原理:当组件读取“未就绪的异步数据”时(如 use Hook 读取 pending 的 Promise),React 会抛出一个 Promise,Suspense 组件捕获这个 Promise,显示 fallback,直到 Promise resolve 后再重新渲染组件;
  • 并发更新的标记:通过 lane(车道模型)标记任务优先级,不同优先级的任务对应不同的“车道”,高优先级车道的任务会优先执行。
3. 学习技巧
  • 先理解“并发”不是“并行”:JavaScript 是单线程,并发是“通过任务切换实现的伪并行”,核心是“中断与恢复”;
  • 用 Suspense + React.lazy 做一个简单的路由懒加载案例,观察组件加载时的 fallback 显示逻辑,再去源码中找 throwException 相关代码,理解 Promise 捕获机制。

总结:学习顺序与避坑指南

1. 推荐学习顺序
  1. 基础架构:明确 Scheduler/Reconciler/Renderer 分层;
  2. Reconciler:先搞懂 Fiber 树和 Diff 算法(最核心的渲染逻辑);
  3. Scheduler:理解任务调度如何支撑 Fiber 中断;
  4. Renderer(react-dom):看 DOM 操作和副作用执行流程;
  5. Hooks 系统:结合 Fiber 节点的 memoizedState 理解状态存储;
  6. Concurrent Mode:最后学高级特性,依赖前面的基础。
2. 避坑指南
  • 不要一开始就看全部代码:React 源码量大(约 10 万行),先聚焦上述核心模块,忽略边缘特性(如 react-test-renderer、服务器端渲染);
  • 用“调试+案例”代替“死读代码”:比如写一个简单的 useState 组件,打断点看 Hooks 如何存储;写一个列表组件,看 Diff 如何执行;
  • 参考官方文档和社区解析:React 官方有「React 设计原理」文档,社区有「Dan Abramov 的博客」(React 核心成员)、「React 源码解析系列」等,可辅助理解。

通过以上模块的学习,能逐步建立 React 从“任务调度”到“DOM 渲染”的完整逻辑链,后续再深入其他特性(如 Redux 集成、SSR)会更轻松。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐