【React】React18.2.0核心源码解读
前言
本文使用 React18.2.0
的源码,如果想回退到某一版本执行git checkout tags/v18.2.0
即可。如果打开源码发现js文件报ts类型错误请看本人另一篇文章:VsCode查看React源码全是类型报错如何解决。
阅读源码的过程:
-
下载源码
-
观察
package.json
使用的依赖以及构建相关的脚本 -
根据
核心API
寻找对应结构- packages/react
- packages/react-dom
- packages/react-reconciler
- packages/scheduler
-
串联整个流程
-
React项目初始化,ReactDOM.render(React18之前)、ReactDOM.createRoot(React18)
-
数据更新是如何触发的,this.setState,setState,forupdate
-
基本API的使用方式
- hooks、useState、useReducer、useId
-
一、ReactElement
React如何通过如下JSX代码生成DOM结构
const Element = (<div>123</div>
)
借助 @babel/plugin-transform-react-jsx-development
进行 Babel
编译,JSX 代码段会变成标准的 React.createElement
调用形式。官方案例链接
React.createElement
的作用是创建 React元素
(JS对象)。
观察源码,可以发现 React 对于开发环境和生产环境的 createElement
做了不同处理。(本文观察的React18.2.0,18.3.0对此进行了小改动)
先观察生产环境下使用的createElementProd
根据传入的参数,通过ReactElement()
创建一个 React 元素
开发环境下的createElementWithValidation
最终也是使用ReactElement()
生成React元素
ReactElement
工厂函数用于创建一个包含类型、属性、引用、唯一标识符等信息的 React 元素(JS对象)。
生产模式下,只会创建一个简单的元素对象;而在开发模式下,会添加额外的调试信息和验证逻辑,比如 key 和 ref 验证、来源追踪等。
使用时直接打印组件,分别对应
查看typeof的标识
查看owner
发现ReactCurrentOwner.current
类型为Fiber
二、Fiber
Fiber
是自 React 16 开始引入的一种新架构,在此之前采用的 Stack Reconciler
会同步地遍历整个组件树,一旦开始渲染,就会阻塞其他任务,直到渲染完成。Fiber
可以将渲染工作拆分为更小的任务单元,每个工作单元只渲染树中的一个节点,并允许在任务之间进行中断和恢复,从而改善了这一问题。
Fiber
使用双缓存机制来管理更新。current tree
代表当前页面显示的 Fiber 树,work-in-progress tree
是当前渲染的新 Fiber 树,当新的 Fiber 树完成时,React 会将其替换为当前树。
1. Fiber工作流程
Fiber工作流程分为两个阶段,分别为 Reconciliation
阶段(调和阶段)以及 Commit
阶段(提交阶段)
Reconciler 阶段「调和阶段」
该阶段会生成Fiber树,得出需要更新的节点信息,可以被中断,去处理更高优先级的任务,比如用户交互和动画。
这个阶段发生在虚拟 DOM,即 Fiber Tree 中,而不会直接影响实际的 DOM。Fiber Tree是链表结构,使用diff算法
将递归遍历变成循环遍历,逐步对比每个节点的状态和属性,构建出一棵新的 Fiber 树(work-in-progress tree)。然后配合requestIdleCallback API
,实现了任务的拆分、中断和恢复。
Commit 阶段「提交阶段」
一旦 work-in-progress
树构建完成,并确定了需要执行的更新,React 会进入 Commit 阶段,将这些变更应用到真实DOM 中。
当所有的 DOM 更新完成后,React 会将 work-in-progress tree
切换为 current tree
,即新的 Fiber 树变成当前页面上展示的树,而之前的 current tree 会被丢弃。这种树的切换类似于双缓存的概念,即始终有一棵树在页面上渲染,而另一棵树则作为工作树进行更新。
该阶段会直接影响真实 DOM,更新操作一旦开始无法被中断,保证了 UI 的一致性和完整性。
三、Hooks
React 使用链表来管理函数组件中的 Hooks,从而确保它们在每次渲染时按照固定的顺序执行和更新。如果强行改变 Hook 的执行顺序则会报错,具体请看本人另一篇文章:为什么Hooks不能出现在判断中。
下面先以使用频率最高的useState
为主线,剩余常用hook下文仍会讲述
1. resolveDispatcher
React 的 Hooks 系统通过 ReactDispatcher
来管理不同生命周期阶段的 Hook 调用。不同的渲染阶段(如初次渲染、更新渲染)会使用不同的 dispatcher
实现,以便处理对应阶段的 Hooks 逻辑。
观察常用的Hook,发现调用了 resolveDispatcher
,这是一个分发器
,主要用于在「函数组件」或自定义 Hook 中获取当前的 ReactDispatcher
。
查看 resolveDispatcher
,它取
出并返回了ReactCurrentDispatcher.current
。
继续查看ReactCurrentDispatcher.current
,它只是一个简单对象,用于标记当前追踪的分发器
。
Fiber
中对ReactCurrentDispatcher.current
进行了「初始化」以及「更新」的处理。
HooksDispatcherOnMount
:负责在组件初次挂载
(即组件首次渲染)时处理 hooks 的调度工作。
HooksDispatcherOnUpdate
: 确保在组件更新
阶段,所有 hooks 能够按照正确的顺序和逻辑被执行,并且能够访问和更新之前存储的状态。
下面分别观察二者:
2. HooksDispatcherOnMount
查看最常用的useState
2.1 mountWordkInProgressHook()
其中 mountWordkInProgressHook()
用于在「函数组件」首次渲染
时创建、初始化和链接 hooks 对象到链表中并更新指向当前工作中的 hook 节点的指针。保证了 React 在管理和调度 hooks 时,能够按照正确的顺序操作每一个 hook,并在后续的更新过程中正确地访问和更新这些 hooks 的状态。
2.1.1 mountWordkInProgressHook 链接Hook对象流程
-
创建 Hook 对象
- memoizedState:用于存储 hook 的状态值,比如 useState 中的状态。
- baseState:表示 hook 的初始状态。
- queue:用于存储更新队列,通常用在像 useState 这样的 hook 中。
- next:指向下一个 hook 对象的引用,形成链表结构。
-
链接 Hook 链表
「创建第一个 hook」 :workInProgressHook
通常为 null,会将firstWorkInProgressHook
指向这个新创建的 hook 对象。
「后续的 hook」:会将新创建的 hook 对象链接到当前链表的末尾
(workInProgressHook.next = hook
),确保 hook 的执行顺序。 -
更新 Hook 指针:
在每次创建完新的 hook 对象后,会更新workInProgressHook
指针,使其指向刚刚创建并链接的 hook。确保下一次mountWorkInProgressHook()
时,能正确地将新 hook 链接到链表的末尾。
2.2 queue
继续往下阅读代码,这一部分是对setState
函数方式赋值的处理。
const [count, setCount] = useState(() => 0)
得到 initialState
后,将其赋值给上一步 mountWordkInProgressHook()
创建的 hook对象 的 memoizedState
和 baseState
。
然后创建 queue
状态更新队列,其中
pending
:存储当前挂起的更新链表,当有新的状态更新时,它们会被追加到这个链表中,等待被处理。lanes
:更新的优先级,NoLanes
是默认值,表示当前没有分配任何特定的优先级。dispatch
:一个函数引用,用于触发状态更新。调用 setState 或 dispatch,实际上就是在触发queue.dispatch
,这会触发一个新的状态更新流程。lastRenderedReducer
:上一次
渲染时使用的reducer
函数,reducer 函数用于计算新的状态,basicStateReducer
是默认的 reducer 表示直接返回新的状态值。lastRenderedState
:组件上一次
渲染时的状态值
,用来确定是否需要触发重新渲染(如果和本次一致则不会重新渲染)。
扩展: queue.lanes
在 React18 之前是通过 expirationTime
实现的,但是 React18 引入了新模型lanes
,它可以「中断更新」而且「排队」、「插队」也更优。
2.3 dispatchSetState()
继续阅读代码,dispatch通过 dispatchSetState()
实现,这个函数根据当前的渲染状态决定如何处理更新,并在需要时触发组件的 「重新渲染」。
代码如下:
参数
- fiber: 当前组件对应的 Fiber 节点(组件的状态和结构)。
- queue: 状态更新队列 UpdateQueue,存储了该组件的所有挂起的状态更新。
- action: 用户触发的状态更新动作,可能是新的状态值或状态更新函数。
逐行分析 dispatchSetState()
const lane = requestUpdateLane(fiber);
获取更新的优先级- 更新update(状态更新的对象)
- 处理渲染
dispatchSetState()
中还有一个很重要的函数:requestEventTime()
,它用于在 React 调度事件时,根据不同的上下文返回合适的时间戳。
继续查看 requestEventTime()
中 now()
的实现:
通过代码可以发现 React 优先使用 performance.now()
提供高精度的时间戳,用于调度和优化渲染过程,对于不支持 performance.now()
的环境则使用Date.now()
。二者的差别可以看本人另一篇文章Date.now()与performance.now()。
2.4 return
return就非常眼熟了,返回的数组元素分别为状态以及修改状态,这里有个小问题,可以看本人另一篇文章:useState为何返回数组而非对象
2.5 basicStateReducer
通过 mountState
和 mountReducer
可以证实 useState
是 useReducer
的 「语法糖」,useReducer
通过参数传递,而useState
通过basicStateReducer
实现状态更新。
查看 basicStateReducer
updateReducer
中处理basicStateReducer
3. HooksDispatcherOnUpdate
更新
updateState
中进行updateReducer
4. useCallback
经过上面的流程,此时已经对useState
工作机制了解了,再来看看useCallback
4.1 挂载
同样是通过mountWorkInProgressHook()
创建、初始化和链接 hooks 对象到链表中并更新指向当前工作中的 hook 节点的指针,判断依赖数组并更新hook状态。
4.2 更新
如果为hook已有状态(更新渲染)、提供了有效依赖数组、依赖数组与前一次状态一致,则沿用上一次缓存的callback,否则采用传入的。
is()
用于比较两个值是否完全一致
即使 NaN
也会视为相等
5. useMemo
再来看看 useMemo
,不同于 useCallback
返回函数,useMemo
针对的是值,其余逻辑一致。
6. useEffect
先来看挂载阶段的mountEffect
6.1 mountEffectImp 和 updateEffectImpl
mountEffectImpl
的任务就是挂载一个新的 useEffect
,并根据依赖数组确定副作用的触发条件。
updateEffectImpl
用于更新 useEffect
不论是mountEffectImpl
还是updateEffectImpl
最终都执行pushEffect
,下面继续查看updateEffectImpl
。
6.1.1 pushEffect
pushEffect
用于创建一个副作用对象,并将它添加到 hook 的链表中。
相关文章:

【React】React18.2.0核心源码解读
前言 本文使用 React18.2.0 的源码,如果想回退到某一版本执行git checkout tags/v18.2.0即可。如果打开源码发现js文件报ts类型错误请看本人另一篇文章:VsCode查看React源码全是类型报错如何解决。 阅读源码的过程: 下载源码 观察 package…...

深度学习-目标检测(四)-Faster R-CNN
目录 一.模型框架 二:步骤详细 1.conv layers 2.RPN 3.anchors 4.cls layer分类 5.reg layer回归 6.Proprosal 7.Rol pooling 8.Classification 三.训练 1.训练RPN网络 2.全连接层部分训练: 都看到这里了,点个赞把!&a…...
MATLAB中的无线通信系统设计有哪些最佳实践
在无线通信系统设计领域,MATLAB提供了一套强大的工具箱,使得系统设计、仿真、测试和分析变得更加高效和精确。本文将探讨MATLAB在无线通信系统设计中的最佳实践,包括信号处理、调制与解调、信道建模、误码率分析以及无线通信标准的实现。 1.…...

Java的发展史与前景
🌈个人主页:Yui_ 🌈Linux专栏:Linux 🌈C语言笔记专栏:C语言笔记 🌈数据结构专栏:数据结构 🌈C专栏:C 文章目录 0. Java语言的发展史1.概述1.1 什么是Java1.2 …...

2024年上海小学生古诗文大会倒计时30多天:做几道今年的官方模拟题
2024年上海市小学生古诗文大会自由报名活动的初赛日期于10月19日开始,距离今天只有34天了。 小学生古诗文大会考什么?怎么考呢?今天好真题就带着大家来做一做官方发布的2024年小学生古诗文大会的模拟题,根据往年的经验࿰…...

IDEA 常用配置和开发插件
件市场中搜索并安装“Git Integration”插件。 一、前言 在本篇文章中我会为大家总结一些我自己常用的配置和开发插件,此外也给大家提供一个建议,可以根据自己的项目需求和个人偏好选择适合的插件。另外,IDEA 也在不断更新,可能会…...

还在为企微联系人烦恼?一招解决!企业微信2024年效率升级全攻略
现在信息多得让人眼花,微信里头那些企业微信的联系人是不是让你头疼? 看着满屏的绿色头像,心里想:“我就想和朋友聊聊天,怎么就这么难?”别急,今天教你个办法,轻松搞定这些小烦恼&am…...
【docker npm】npm 私库
1.部署环境 window 11 x64Docker Desktop 4.34.1 (166053) Docker Engine v27.2.0 1.1.Docker 镜像源 1.1.1.Docker Engine 配置 {"builder": {"features": {"buildkit": true},"gc": {"defaultKeepStorage": "32…...

完整gpt应用(自用)
qrc.py 把gpt_qrc.qrc转化成gpt_qrc.py pyrcc5 -o icons_rc.py icons.qrc <RCC><qresource prefix"img"><file>img/53.png</file><file>img/ai.png</file><file>img/关闭.png</file><file>img/最小化.png&l…...

【信息论基础第二讲】离散信源的数学模型及其信息测度包括信源的分类、信源的数学模型、离散信源的信息测度、二元信源的条件熵联合熵
一、信源的分类 二、信源的数学模型 1、信源的概念 在通信系统中,收信者在未收到信息以前,对信源发出什么消息是不确定的、随机的、因此我们可以用随机变量、随机序列或者随机过程来描述信源的输出。严格地说,用概率空间来描述信源输出。 …...
在 Spring Boot 项目中连接 IBM AS/400 数据库——详细案例教程
文章目录 1. 添加 jt400 依赖2. 下载 jt400 驱动包依赖下载手动下载下载地址:手动下载 JAR 的步骤: 3. 配置 application.properties 或 application.yml(1)application.properties(2)application.yml 4. 数…...

VUE + NODE 历史版本安装
以node 12.20.0为例子,想下载哪个版本,后面写哪个版本 https://registry.npmmirror.com/binary.html?pathnode/v12.20.0/ 安装国内镜像7.1.0 cnpm npm install -g cnpm7.1.0 -g --registryhttps://registry.npmmirror.com 安装vue脚手架4.5.15 cnpm …...
git reset 几点疑问
疑问:使用 git reset --hard <commit-hash-from-branch-B> 将工作区状态reset为其他branch的某点。 如果当前工作区的分支(比如 branch A)上使用 git reset --hard 将其状态重置为另一个分支(比如 branch B)的某…...

Rust Windows下编译 静态链接VCRuntime140.dll
Rust 编译出来的exe默认动态链接VC运行库,分发电脑上需要安装有Microsoft Visual C Redistributable for Visual Studio 2015运行库。 编译时能静态链接进去,就省去客户端未安装运行库的问题。方法如下: 只需在当前根目录下新建.cargo\config.toml&#…...
从“天宫课堂”到人工智能:中国少儿编程的未来在哪里?
近日,中国载人航天“天宫课堂”第三次开讲,激发了全国数百万青少年对科技的热情。从航天技术到人工智能,科技的快速发展正不断改变我们的生活,也让越来越多的家长意识到,未来属于那些掌握编程和创新思维的孩子。与其让…...
ARM base instruction -- blr
BLR Branch with Link to Register calls a subroutine at an address in a register, setting register X30 to PC4. 带寄存器链接的分支在寄存器中的某个地址调用一个子程序,将寄存器 X30 (lr) 设置为 PC4。 BLR <Xn> BLR 跳转到reg内容地址,…...

宠物猫领养馆会员管理系统---附源码72579
目录 1 绪论 1.1 课题目的与意义 1.2国内外研究现状 1.3论文结构与章节安排 1.4 express框架介绍 2 宠物猫领养馆会员管理系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 …...

驾驶员注意力分神状态检测系统源码分享
驾驶员注意力分神状态检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of …...

基于less和scss 循环生成css
效果 一、less代码 复制代码 item-count: 12; // 生成多少个 .item 类.item-loop(n) when (n > 0) {.icon{n} {background: url(../../assets/images/menu/icon{n}.png) no-repeat;background-size: 100% 100%;}.item-loop(n - 1);}.item-loop(item-count);二、scss代码 f…...

opencv之Canny边缘检测
文章目录 前言1.应用高斯滤波去除图像噪声2.计算梯度3.非极大值抑制4.应用双阈值确定边缘5.Canny函数及使用 前言 Canny边缘检测是一种流行的边缘检测算法,用于检测图像中的边缘。它通过一系列步骤将图像中的像素边缘突出显示出来,主要分为以下几个步骤…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
pycharm 设置环境出错
pycharm 设置环境出错 pycharm 新建项目,设置虚拟环境,出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...