【React】React18核心源码解读

前言

本文使用 React18.2.0 的源码,如果想回退到某一版本执行git checkout tags/v18.2.0即可。如果打开源码发现js文件报ts类型错误请看本人另一篇文章:VsCode查看React源码全是类型报错如何解决。

阅读源码的过程:

  1. 下载源码

  2. 观察 package.json 使用的依赖以及构建相关的脚本

  3. 根据 核心API 寻找对应结构

    • packages/react
    • packages/react-dom
    • packages/react-reconciler
    • packages/scheduler
  4. 串联整个流程

    • 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对象流程

在这里插入图片描述

  1. 创建 Hook 对象

    • memoizedState:用于存储 hook 的状态值,比如 useState 中的状态。
    • baseState:表示 hook 的初始状态。
    • queue:用于存储更新队列,通常用在像 useState 这样的 hook 中。
    • next:指向下一个 hook 对象的引用,形成链表结构。
  2. 链接 Hook 链表
    「创建第一个 hook」 :workInProgressHook 通常为 null,会将 firstWorkInProgressHook 指向这个新创建的 hook 对象。
    「后续的 hook」:会将新创建的 hook 对象链接到当前链表的末尾workInProgressHook.next = hook),确保 hook 的执行顺序。

  3. 更新 Hook 指针:
    在每次创建完新的 hook 对象后,会更新 workInProgressHook 指针,使其指向刚刚创建并链接的 hook。确保下一次 mountWorkInProgressHook() 时,能正确地将新 hook 链接到链表的末尾。

2.2 queue

继续往下阅读代码,这一部分是对setState函数方式赋值的处理。

const [count, setCount] = useState(() => 0)

在这里插入图片描述
得到 initialState 后,将其赋值给上一步 mountWordkInProgressHook() 创建的 hook对象 的 memoizedStatebaseState

然后创建 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

通过 mountStatemountReducer 可以证实 useStateuseReducer「语法糖」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 的链表中。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/883755.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

亚信安全DeepSecurity中标知名寿险机构云主机安全项目

近日&#xff0c;亚信安全DeepSecurity成功中标国内知名寿险机构的云主机安全项目。亚信安全凭借在云主机安全防护领域的突出技术优势&#xff0c;结合安全运营的能力&#xff0c;以“实战化”为指导&#xff0c;为用户提供无惧威胁攻击、无忧安全运营的一站式云安全体系&#…

前端 eslint 配置,以及在git提交之前自动format

目录 1、配置eslint步骤 1、eslint安装配置步骤 2、配置scripts步骤 3、测试eslint 2、配置git-hook1、安装环境2、最终效果 众所周知&#xff0c;前端项目可以在报很多error的情况下运行。但是良好的代码规范仍然有利于项目的开发维护&#xff0c;这里提供我的规范&#xff0c…

vue使用阿里svg图标

最近开发项目的写前端时候&#xff0c;发现element的图标库不能满足我的需求&#xff0c;当然&#xff0c;大部分是够用的&#xff0c;不过某些特定按钮没有合适的图标,我是发现没有批量导入按钮的图标&#xff0c;所以找了阿里的适量图标库来使用 阿里矢量图标库 阿里矢量图…

深入了解 kotlinx-datetime:配置与使用指南

深入了解 kotlinx-datetime&#xff1a;配置与使用指南 在Kotlin多平台开发中&#xff0c;处理日期和时间是常见的需求。kotlinx-datetime库提供了强大且简洁的API来帮助开发者应对这一挑战。本文将详细介绍如何配置kotlinx-datetime库&#xff0c;并通过生动的示例演示其核心…

ventoy安装和使用

本文涉及一种快捷安装不同版本操作系统的工具的安装和使用。 背景 笔者以前安装系统&#xff0c;一般是先下载系统ISO镜像&#xff0c;使用UltraISO将镜像写入U盘&#xff0c;然后用U盘启动。 近年来着手国产化的适配工作&#xff0c;当然因故无法接触核对业务&#xff0c;主…

《Python游戏编程入门》注-第4章1

《Python游戏编程入门》的第4章是“用户输入&#xff1a;Bomb Cathcer游戏”&#xff0c;通过轮询键盘和鼠标设备状态实现Bomb Cathcer游戏。 1 Bomb Cathcer游戏介绍 “4.1 认识Bomb Cathcer游戏”内容介绍了Bomb Cathcer游戏的玩法&#xff0c;即通过鼠标来控制红色“挡板”…

Unity3D学习FPS游戏(5)武器瞄准和准星

前言&#xff1a;一个FPS游戏最基本的角色控制&#xff0c;在前面几篇已经基本实现&#xff0c;接下来我们将进入到武器篇。本篇将实现武器武器瞄准和准星。 武器瞄准和准星 武器瞄准思路实现效果 武器准星思路实现效果 补充知识协程线性插值 武器瞄准 思路 FPS游戏在准备开枪…

vue-echarts使用

vue-echarts使用 排名柱状图示例代码 汇总示例代码 平均时效示例代码 全图 排名柱状图 示例 代码 // 排名趋势<!-- 排名数据趋势图 --><div class"rank"><div class"rank_title"><div class"rank_title_left"><spa…

【04】RabbitMQ的集群机制

1、RabbitMQ的性能监控 关于RabbitMQ的性能监控&#xff0c;在管理控制台中提供了非常丰富的展示。例如&#xff1a;首页这个整体监控页面&#xff0c;就展示了非常多详细的信息&#xff1a; 还包括消息的生产消费频率、关键组件的使用情况等等非常多的消息。都可以在这个管理…

Segugio:一款针对恶意软件的进程执行跟踪与安全分析工具

关于Segugio Segugio是一款功能强大的恶意软件安全分析工具&#xff0c;该工具允许我们轻松分析恶意软件执行的关键步骤&#xff0c;并对其进行跟踪分析和安全审计。 Segugio允许执行和跟踪恶意软件感染过程中的关键步骤&#xff0c;其中包括从点击第一阶段到提取恶意软件的最…

YOLO-FaceV2:A Scale and Occlusion Aware Face Detector

摘要 近年来&#xff0c;基于深度学习的人脸检测算法取得了巨大进展。这些算法一般可以分为两类&#xff0c;即像Faster R-CNN这样的两阶段检测器和像YOLO这样的一阶段检测器。由于一阶段检测器在精度和速度之间取得了更好的平衡&#xff0c;因此已被广泛应用于多种场景。在本…

贪心算法与盛雨水问题

啥是盛雨水问题&#xff1f;给个图就熟悉了 欸&#xff1f; 这其中的关键在于&#xff1a; 1. 容量2D化就是长 * 宽 2. 木桶效应&#xff1a;宽取决于短板。 那我们来分析&#xff0c;怎么样能达到最佳的结果呢&#xff1f;穷举一下所有可能性不就好了&#xff1f;每两个板子…

OAK相机的RGB-D彩色相机去畸变做对齐

▌低畸变标准镜头的OAK相机RGB-D对齐的方法 OAK相机内置的RGB-D管道会自动将深度图和RGB图对齐。其思想是将深度图像中的每个像素与彩色图像中对应的相应像素对齐。产生的RGB-D图像可以用于OAK内置的图像识别模型将识别到的2D物体自动映射到三维空间中去&#xff0c;或者产生的…

Information Theoretical Estimators (ITE) Toolbox的使用(MATLAB)

Information Theoretical Estimators (ITE) Toolbox是什么 官方文档&#xff1a; ITE is can estimate several entropy, mutual information, divergence, association measures, cross quantities and kernels on distributions. Thanks to its highly modular design, ITE …

STM32G474硬件CRC16和软件CRC16校验

1、硬件CRC校验和软件CRC校验的多项式&#xff0c;以及初始值 #define CRC_Hardware_POLYNOMIAL_16B 0x8005 //硬件CRC多项式为0x8005 //CRC16x^16 x^15 x^2 1&#xff0c;因为bit151,bit21,bit01&#xff0c;所以正向校验的多项式的值为0x8005 //CRC校验分为正向校验和反…

UWA Gears:Frame Capture模式 - 着色器查看器

UWA Gears 是UWA最新发布的无SDK性能分析工具。针对移动平台&#xff0c;提供了实时监测和截帧分析功能&#xff0c;帮助您精准定位性能热点&#xff0c;提升应用的整体表现。 在上周的文章中&#xff0c;我们详细介绍了网格查看器的功能&#xff0c;介绍如何通过网格数据优化…

微服务电商平台课程三:基础环境搭建

后端基础环境 工具版本号功能说明下载JDK1.8java编译运行的基本环境Java Downloads | Oracledocker27.0.3容器化部署Windows | Docker Docsgit2.46.2代码版本管理&#xff0c;多人协作代码开发Git for Windowsmaven3.9.9服务的依赖管理Maven – Download Apache MavenMySQL5.7…

第十四章 章节练习echarts饼图渲染

目录 一、引言 二、完整代码 三、总结 一、引言 通过前面几个章节的学习&#xff0c;再结合日常项目中经常会使用到的echarts图&#xff0c;来完整以下功能需求&#xff0c;增强对知识点的巩固&#xff1a; 1. 基本渲染 2. 添加功能 3. 删除功能 4. 饼图渲染 运行效果图…

厨艺爱好者的在线互动平台:Spring Boot实现

摘 要 使用旧方法对厨艺交流信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在厨艺交流信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。 这次开发的厨艺交流平台功能…

WUP-MY-POS-PRINTER 旻佑热敏打印机票据打印uniapp插件使用说明

插件地址&#xff1a;WUP-MY-POS-PRINTER 旻佑热敏打印机票据打印安卓库 简介 本插件主要用于旻佑热敏打印机打印票据&#xff0c;不支持标签打印。适用于旻佑的各型支持票据打印的热敏打印机。本插件开发时使用的打印机型号为MY-805嵌入式面板打印机&#xff0c;其他型号请先…