当前位置: 首页 > news >正文

React源码分析8-状态更新的优先级机制

这是我的剖析 React 源码的第二篇文章,如果你没有阅读过之前的文章,请务必先阅读一下 第一篇文章 中提到的一些注意事项,能帮助你更好地阅读源码。

文章相关资料

  • React 16.8.6 源码中文注释,这个链接是文章的核心,文中的具体代码及代码行数都是依托于这个仓库
  • 热身篇
  • render 流程(二)

现在请大家打开 我的代码 并定位到 react-dom 文件夹下的 src 中的 ReactDOM.js 文件,今天的内容会从这里开始。

render

想必大家在写 React 项目的时候都写过类似的代码

ReactDOM.render(<APP />, document.getElementById('root')

这句代码告诉了 React 应用我们想在容器中渲染出一个组件,这通常也是一个 React 应用的入口代码,接下来我们就来梳理整个 render 的流程,并且会分为几篇文章来讲解,因为流程实在太长了。

首先请大家先定位到 ReactDOM.js 文件的第 702 行代码,开始今天的旅程。

这部分代码其实没啥好说的,唯一需要注意的是在调用 legacyRenderSubtreeIntoContainer 函数时写死了第四个参数 forceHydratefalse。这个参数为 true 时表明了是服务端渲染,因为我们分析的是客户端渲染,因此后面有关这部分的内容也不会再展开。

接下来进入 legacyRenderSubtreeIntoContainer 函数中,这部分代码分为两块来讲。第一部分是没有 root 之前我们首先需要创建一个 root(对应这篇文章),第二部分是有 root 之后的渲染流程(对应接下来的文章)。

一开始进来函数的时候肯定是没有 root 的,因此我们需要去创建一个 root,大家可以发现这个 root 对象同样也被挂载在了 container._reactRootContainer 上,也就是我们的 DOM 容器上。
如果你手边有 React 项目的话,在控制台键入如下代码就可以看到这个 root 对象了。

document.querySelector('#root')._reactRootContainer

大家可以看到 rootReactRoot 构造函数构造出来的,并且内部有一个 _internalRoot 对象,这个对象是本文接下来要重点介绍的 fiber 对象,接下来我们就来一窥究竟吧。

首先还是和上文中提到的 forceHydrate 属性相关的内容,不需要管这部分,反正 shouldHydrate 肯定为 false

接下来是将容器内部的节点全部移除,一般来说我们都是这样写一个容器的的

<div id='root'></div>

这样的形式肯定就不需要去移除子节点了,这也侧面说明了一点那就是容器内部不要含有任何的子节点。一是肯定会被移除掉,二来还要进行 DOM 操作,可能还会涉及到重绘回流等等。

最后就是创建了一个 ReactRoot 对象并返回。接下来的内容中我们会看到好几个 root,可能会有点绕。

ReactRoot 构造函数内部就进行了一步操作,那就是创建了一个 FiberRoot 对象,并挂载到了 _internalRoot 上。和 DOM 树一样,fiber 也会构建出一个树结构(每个 DOM 节点一定对应着一个 fiber 对象),FiberRoot 就是整个 fiber 树的根节点,接下来的内容里我们将学习到关于 fiber 相关的内容。这里提及一点,fiber 和 Fiber 是两个不一样的东西,前者代表着数据结构,后者代表着新的架构。

createFiberRoot 函数内部,分别创建了两个 root,一个 root 叫做 FiberRoot,另一个 root 叫做 RootFiber,并且它们两者还是相互引用的。

这两个对象内部拥有着数十个属性,现在我们没有必要一一去了解它们各自有什么用处,在当下只需要了解少部分属性即可,其他的属性我们会在以后的文章中了解到它们的用处。

对于 FiberRoot 对象来说,我们现在只需要了解两个属性,分别是 containerInfocurrent。前者代表着容器信息,也就是我们的 document.querySelector('#root');后者指向 RootFiber

对于 RootFiber 对象来说,我们需要了解的属性稍微多点

function FiberNode(tag: WorkTag,pendingProps: mixed,key: null | string,mode: TypeOfMode,
) {this.stateNode = null;this.return = null;this.child = null;this.sibling = null;this.effectTag = NoEffect;this.alternate = null;
}

stateNode 上文中已经讲过了,这里就不再赘述。

returnchildsibling 这三个属性很重要,它们是构成 fiber 树的主体数据结构。fiber 树其实是一个单链表树结构,returnchild 分别对应着树的父子节点,并且父节点只有一个 child 指向它的第一个子节点,即便是父节点有好多个子节点。那么多个子节点如何连接起来呢?答案是 sibling,每个子节点都有一个 sibling 属性指向着下一个子节点,都有一个 return 属性指向着父节点。这么说可能有点绕,我们通过图来了解一下这个 fiber 树的结构。

const APP = () => (<div><span></span><span></span></div>
)
ReactDom.render(<APP/>, document.querySelector('#root'))

假如说我们需要渲染出以上组件,那么它们对应的 fiber 树应该长这样

从图中我们可以看到,每个组件或者 DOM 节点都会对应着一个 fiber 对象。另外你手边有 React 项目的话,也可以在控制台输入如下代码,查看 fiber 树的整个结构。

// 对应着 FiberRoot
const fiber = document.querySelector('#root')._reactRootContainer._internalRoot

另外两个属性在本文中虽然用不上,但是看源码的时候笔者觉得很有意思,就打算拿出来说一下。

在说 effectTag 之前,我们先来了解下啥是 effect,简单来说就是 DOM 的一些操作,比如增删改,那么 effectTag 就是来记录所有的 effect 的,但是这个记录是通过位运算来实现的,这里 是 effectTag 相关的二进制内容。

如果我们想新增一个 effect 的话,可以这样写 effectTag |= Update;如果我们想删除一个 effect 的话,可以这样写 effectTag &= ~Update

最后是 alternate 属性。其实在一个 React 应用中,通常来说都有两个 fiebr 树,一个叫做 old tree,另一个叫做 workInProgress tree。前者对应着已经渲染好的 DOM 树,后者是正在执行更新中的 fiber tree,还能便于中断后恢复。两棵树的节点互相引用,便于共享一些内部的属性,减少内存的开销。毕竟前文说过每个组件或 DOM 都会对应着一个 fiber 对象,应用很大的话组成的 fiber 树也会很大,如果两棵树都是各自把一些相同的属性创建一遍的话,会损失不少的内存空间及性能。

当更新结束以后,workInProgress tree 会将 old tree 替换掉,这种做法称之为 double buffering,这也是性能优化里的一种做法,有兴趣的同学可以自行查找资料。

相关参考视频讲解:进入学习

总结

以上就是本文的全部内容了,最后通过一张流程图总结一下这篇文章的内容。

最后

阅读源码是一个很枯燥的过程,但是收益也是巨大的。如果你在阅读的过程中有任何的问题,都欢迎你在评论区与我交流。

另外写这系列是个很耗时的工程,需要维护代码注释,还得把文章写得尽量让读者看懂,最后还得配上画图,如果你觉得文章看着还行,就请不要吝啬你的点赞。

下一篇文章还是 render 流程相关的内容。

相关文章:

React源码分析8-状态更新的优先级机制

这是我的剖析 React 源码的第二篇文章&#xff0c;如果你没有阅读过之前的文章&#xff0c;请务必先阅读一下 第一篇文章 中提到的一些注意事项&#xff0c;能帮助你更好地阅读源码。 文章相关资料 React 16.8.6 源码中文注释&#xff0c;这个链接是文章的核心&#xff0c;文…...

如何在ChatGPT的API中支持多轮对话

一、问题 ChatGPT的API支持多轮对话。可以使用API将用户的输入发送到ChatGPT模型中&#xff0c;然后将模型生成的响应返回给用户&#xff0c;从而实现多轮对话。可以在每个轮次中保留用户之前的输入和模型生成的响应&#xff0c;以便将其传递给下一轮对话。这种方式可以实现更…...

华为OD机试模拟题 用 C++ 实现 - 猜字谜(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明猜字谜题目输入输出描述备注示例一输入输出示例二输入输出思路Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,...

Containerd容器运行时将会替换Docker?

文章目录一、什么是Containerd&#xff1f;二、Containerd有哪些功能&#xff1f;三、Containerd与Docker的区别四、Containerd是否会替换Docker&#xff1f;五、Containerd安装、部署和使用公众号&#xff1a; MCNU云原生&#xff0c;欢迎微信搜索关注&#xff0c;更多干货&am…...

java虚拟机中对象创建过程

java虚拟机中对象创建过程 我们平常创建一个对象&#xff0c;仅仅只是使用new关键字new一个对象&#xff0c;这样一个对象就被创建了&#xff0c;但是在我们使用new关键字创建对象的时候&#xff0c;在java虚拟机中一个对象是如何从无到有被创建的呢&#xff0c;我们接下来就来…...

3485. 最大异或和

Powered by:NEFU AB-IN Link 文章目录3485. 最大异或和题意思路代码3485. 最大异或和 题意 给定一个非负整数数列 a&#xff0c;初始长度为 N。 请在所有长度不超过 M的连续子数组中&#xff0c;找出子数组异或和的最大值。 子数组的异或和即为子数组中所有元素按位异或得到的…...

SpringBoot:SpringBoot配置文件.properties、.yml 和 .ymal(2)

SpringBoot配置文件1. 配置文件格式1.1 application.properties配置文件1.2 application.yml配置文件1.3 application.yaml配置文件1.4 三种配置文件优先级和区别2. yaml格式2.1 语法规则2.2 yaml书写2.2.1 字面量&#xff1a;单个的、不可拆分的值2.2.2 数组&#xff1a;一组按…...

QT 学习之QPA

QT 为实现支持多平台&#xff0c;实现如下类虚函数 Class Overview QPlatformIntegration QAbstractEventDispatcherQPlatformAccessibilityQPlatformBackingStoreQPlatformClipboardQPlatformCursorQPlatformDragQPlatformFontDatabaseQPlatformGraphicsBufferQPlatformInput…...

Pytorch中FLOPs和Params计算

文章目录一. 含义二. 使用thop库计算FLOPs和Params三. 注意四. 相关链接一. 含义 FLOPs&#xff08;计算量&#xff09;&#xff1a;注意s小写&#xff0c;是floating point operations的缩写&#xff08;这里的小s则表示复数&#xff09;&#xff0c;表示浮点运算数&#xff…...

DP1621国产LCD驱动芯片兼容替代HT1621B

目录DP1621简介DP1621芯片特性DP1621简介 DP1621是点阵式存储映射的LCD驱动器芯片&#xff0c;可支持最大128点&#xff08;32SEG * 4COM&#xff09;的 LCD屏&#xff0c;也支持2COM和3COM的LCD屏。单片机可通过3/4个通信脚配置显示参数和发送显示数据&#xff0c;也可通过指…...

Linux 用户管理

用户管理 useradd新增用户 格式&#xff1a;useradd [参数] 用户名称 常用参数&#xff1a; -c comment 指定一段注释性描述。 -d 目录 指定用户主目录&#xff0c;如果此目录不存在&#xff0c;则同时使用-m选项&#xff0c;可以创建主目录。 -g 用户组 指定用户所属的用户组…...

前端vue面试题(持续更新中)

vue-router中如何保护路由 分析 路由保护在应用开发过程中非常重要&#xff0c;几乎每个应用都要做各种路由权限管理&#xff0c;因此相当考察使用者基本功。 体验 全局守卫&#xff1a; const router createRouter({ ... }) ​ router.beforeEach((to, from) > {// .…...

Java查漏补缺-从入门到精通汇总

Java查漏补缺&#xff08;01&#xff09;计算机的硬件与软件、软件相关介绍、计算机编程语言、Java语言概述、Java开发环境搭建、Java开发工具、注释、API文档、JVM Java查漏补缺&#xff08;02&#xff09;关键字、标识符、变量、基本数据类型介绍、基本数据类型变量间运算规…...

软件测试2年半的我,谈谈自己的理解...

软件测试两年半的我&#xff0c;谈谈自己的理解从2020年7月毕业&#xff0c;就成为一名测试仔。日子混了一鲲年&#xff0c;感觉需要好好梳理一下自己的职业道路了&#xff0c;回顾与总结下吧。一、测试的定位做事嘛&#xff0c;搞清楚自己的定位很重要。要搞清楚自己的定位&am…...

什么是SAS硬盘

什么是SAS硬盘SAS是新一代的SCSI技术&#xff0c;和Serial ATA(SATA)硬盘都是采用串行技术&#xff0c;以获得更高的传输速度&#xff0c;并通过缩短连结线改善内部空间等。SAS是并行SCSI接口之后开发出的全新接口。此接口的设计是为了改善存储系统的效能、可用性和扩充性&…...

一文理解服务端渲染SSR的原理,附实战基于vite和webpack打造React和Vue的SSR开发环境

SSR和CSR 首先&#xff0c;我们先要了解什么是SSR和CSR&#xff0c;SSR是服务端渲染&#xff0c;CSR是客户端渲染&#xff0c;服务端渲染是指 HTTP 服务器直接根据用户的请求&#xff0c;获取数据&#xff0c;生成完整的 HTML 页面返回给客户端&#xff08;浏览器&#xff09;展…...

Matlab 实用小函数汇总

文章目录Part.I 元胞相关Chap.I 创建空 char 型元胞Part.II 矩阵相关Chap.I 矩阵插入元素Part.III 字符串相关Chap.I 获取一个文件夹下所有文件的文件名的部分内容Part.IV 结构体相关Chap.I 读取结构体Chap.II 取结构体中某一字段的所有值本篇博文记录一些笔者使用 Matlab 时&a…...

Echarts 仪表盘倾斜一定角度显示,非中间对称

第024个点击查看专栏目录大多数的情况下&#xff0c;制作的仪表盘都是中规中矩&#xff0c;横向中间对称&#xff0c;但是生活中的汽车&#xff0c;摩托车等仪表盘确是要倾斜一定角度的&#xff0c;Echarts中我们就模拟一个带有倾斜角度的仪表盘。核心代码见示例源代码 文章目录…...

Vue中如何利用websocket实现实时通讯

首先我们可以先做一个简单的例子来学习一下简单的websocket模拟聊天对话的功能 原理很简单&#xff0c;有点像VUE中的EventBus&#xff0c;用emit和on传来传去 首先我们可以先去自己去用node搭建一个本地服务器 步骤如下 1.新建一个app.js&#xff0c;然后创建pagejson.js文…...

​力扣解法汇总1144. 递减元素使数组呈锯齿状

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 给你一个整数数组 nums&#xff0c;每次 操作 会从中选择一个元素并 将该元素的…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...

算法—栈系列

一&#xff1a;删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...

深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学

一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件&#xff0c;其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时&#xff0c;价带电子受激发跃迁至导带&#xff0c;形成电子-空穴对&#xff0c;导致材料电导率显著提升。…...

LeetCode - 148. 排序链表

目录 题目 思路 基本情况检查 复杂度分析 执行示例 读者可能出的错误 正确的写法 题目 148. 排序链表 - 力扣&#xff08;LeetCode&#xff09; 思路 链表归并排序采用"分治"的策略&#xff0c;主要分为三个步骤&#xff1a; 分割&#xff1a;将链表从中间…...

大模型智能体核心技术:CoT与ReAct深度解析

**导读&#xff1a;**在当今AI技术快速发展的背景下&#xff0c;大模型的推理能力和可解释性成为业界关注的焦点。本文深入解析了两项核心技术&#xff1a;CoT&#xff08;思维链&#xff09;和ReAct&#xff08;推理与行动&#xff09;&#xff0c;这两种方法正在重新定义大模…...

调试快捷键 pycharm vscode

目录 调试快捷键 pycharm vscode 修改快捷键 方法 1&#xff1a;通过菜单打开 方法 2&#xff1a;用快捷键打开 调试快捷键 pycharm Resume Program F9 Step Over F8 两个离的比较近&#xff0c;比较方便&#xff0c;比vscode的好。 vscode Continue F5 改为F9 S…...