React进阶之React核心源码解析(二)

React核心源码解析

  • diff
    • 单一节点比较diff
    • 多节点比较diff
      • 两轮遍历比较
        • 第一轮比较
        • 第二轮比较
  • Update 状态更新
  • Concurrent Mode

diff

一共两个阶段

  • render:内存中的更新,主要是通过递归的过程,来将react变化的部分,在内存中找到哪些元素是需要发生变化的,针对需要发生变化的内容,会打上标,也就是Update,针对这些需要Update的组件,会拿着上一次与当前这一次的节点,通过Fiber(专门的数据结构存储当前的组件/模块)进行此次与上一次元素更新之间对比。
    diff 的过程,其实就是Update Fiber的过程,diff的结果就是生成一个经过Update更新之后的新的Fiber的节点。
  • commit:同步的过程,同步渲染到浏览器端/客户端的过程
  1. 针对不同类型的元素

<div><Component />	
</div>

<span><Component />	
</span>

这是不同类型元素的展示,在Fiber中,定义当前组件类型不一致的时候,是需要将当前树全部销毁重建的
因此,在开发过程中,需要尽可能减少元素的变化。

  1. 针对相同类型元素

<ul className="hello"><li>1</li>	<li>2</li>	
</ul>

<ul className="world"><li>3</li>	<li>4</li><li>5</li>		
</ul>

就是在render的阶段中,相同类型的节点进行diff判断,并不会立马更新。生成mutation要变化的内容,针对不同diff进行汇总,然后在commit过程中,对dom的节点直接进行操作。

但是 上述例子中,如果是针对第一个元素<li>之前进行添加的话,会将所有的子列表全部销毁掉,重新创建,
就比如,从

<ul className="hello"><li>1</li>	<li>2</li>	
</ul>

<ul className="world"><li>3</li>	<li>1</li><li>2</li>		
</ul>

这样的情况
这时候,通过key的方式去解决,并且尽可能保证每个key是唯一的,不要去使用index作为key来传输

跟一个真实DOM相关联的是:

  • current fiber:当前的fiber节点,表达当前DOM的 内存对象
  • workInProgress fiber:内存中变化的节点,表达接下来在内存中需要进行操作的对象
  • DOM:在commit过程中生成的真实的dom
  • JSX:真实代码

通过current fiberJSX比较,更新到workInProgress fiber,进行current fiber和workInProgress fiber的互换,拿着workInProgress fiber替换生成到真实DOM

  1. 同级元素的比较
  2. 不同类型元素 销毁当前节点&所有子节点
  3. key
// 根据newChild类型选择不同diff函数处理
function reconcileChildFibers(returnFiber: Fiber,currentFirstChild: Fiber | null,newChild: any,
): Fiber | null {//这里的newChild是fiber节点,不是dom元素了const isObject = typeof newChild === 'object' && newChild !== null;//如果是对象的话,那么就是单一的节点if (isObject) {// object类型,可能是 REACT_ELEMENT_TYPE 或 REACT_PORTAL_TYPEswitch (newChild.$$typeof) {case REACT_ELEMENT_TYPE:// 调用 reconcileSingleElement 处理// // ...省略其他case}}if (typeof newChild === 'string' || typeof newChild === 'number') {// 调用 reconcileSingleTextNode 处理// ...省略}//如果是数组的话,则是多个节点if (isArray(newChild)) {// 调用 reconcileChildrenArray 处理// ...省略}// 一些其他情况调用处理函数// ...省略// 以上都没有命中,删除节点return deleteRemainingChildren(returnFiber, currentFirstChild);
}

单一节点比较diff

function reconcileSingleElement(returnFiber: Fiber,currentFirstChild: Fiber | null,element: ReactElement
): Fiber {const key = element.key;let child = currentFirstChild;// 首先判断是否存在对应DOM节点while (child !== null) {// 上一次更新存在DOM节点,接下来判断是否可复用// 首先比较key是否相同if (child.key === key) {// key相同,接下来比较type是否相同switch (child.tag) {// ...省略casedefault: {if (child.elementType === element.type) {// type相同则表示可以复用// 返回复用的fiberreturn existing;}// type不同则跳出switchbreak;}}// 代码执行到这里代表:key相同但是type不同// 将该fiber及其兄弟fiber标记为删除deleteRemainingChildren(returnFiber, child);break;} else {// key不同,将该fiber标记为删除deleteChild(returnFiber, child);}child = child.sibling;}// 创建新Fiber,并返回 ...省略
}

针对单一的节点:

  • key是否一样
    • 一样
      • type 一样:同一个DOM
      • type不一样:当前节点和所有的兄弟节点sibling全都删除
    • 不一样
      • 直接删除

举例:
全部删除:

// 更新前
<div>a</div>
// 更新后
<p>a</p>// 更新前
<div key="xxx">a</div>
// 更新后
<div key="ooo">a</div>

只更新内容:

// 更新前
<div key="xxx">a</div>
// 更新后
<div key="xxx">b</div>

多节点比较diff

isArray方法比较

  1. 节点更新
// 更新前
<ul><li key="0" className="before">0<li><li key="1">1<li>
</ul>// 更新后 情况1 —— 节点属性变化
<ul><li key="0" className="after">0<li><li key="1">1<li>
</ul>// 更新后 情况2 —— 节点类型更新
<ul><div key="0">0</div><li key="1">1<li>
</ul>

节点属性变化,只需要更新即可
节点类型变化,则当前<li>和兄弟节点全部推倒重建

  1. 节点新增和删除
// 更新前
<ul><li key="0">0<li><li key="1">1<li>
</ul>// 更新后 情况1 —— 新增节点
<ul><li key="0">0<li><li key="1">1<li><li key="2">2<li>
</ul>// 更新后 情况2 —— 删除节点
<ul><li key="1">1<li>
</ul>
  1. 节点位置变化,顺序调整
// 更新前
<ul><li key="0">0<li><li key="1">1<li>
</ul>// 更新后
<ul><li key="1">1<li><li key="0">0<li>
</ul>

这里key不一样,全部删除重建

两轮遍历比较

第一轮比较
  1. let i = 0,遍历newChildren,将newChildren[i]与oldFiber比较,判断DOM节点是否可复用;
    JSX:newChildren[i]
    currentFiber:oldFiber
  2. 如果可复用,i++,继续比较newChildren[i]与oldFiber.sibling,可以复用则继续遍历;
  3. 如果不可复用,分两种情况:
    a. key不同导致不可复用,立即跳出整个遍历,第一轮遍历结束;
    b. key相同type不同导致不可复用,会将oldFiber标记mutation为DELETION,并继续遍历;
  4. 如果newChildren遍历完(即 i === newChildren.length - 1 )或者oldFiber遍历完(即oldFiber.sibling === null),跳出遍历,第一轮遍历结束;
// 更新前
<li key="0">0</li>
<li key="1">1</li>
<li key="2">2</li>// 更新后
<li key="0">0</li>
<li key="2">1</li>
<li key="1">2</li>// 第一个节点可复用,遍历到key === 2的节点发现key改变,不可复用
// 跳出遍历,等待第二轮遍历处理// oldFiber: key === 1、key === 2未遍历
// newChildren剩下key === 2、key === 1未遍历
// 更新前
<li key="0" className="a">0</li>
<li key="1" className="b">1</li>// 更新后 情况1 —— newChildren与oldFiber都遍历完
<li key="0" className="aa">0</li>
<li key="1" className="bb">1</li>// 更新后 情况2 —— newChildren没遍历完,oldFiber遍历完
// newChildren剩下 key==="2" 未遍历
<li key="0" className="aa">0</li>
<li key="1" className="bb">1</li>
<li key="2" className="cc">2</li>// 更新后 情况3 —— newChildren遍历完,oldFiber没遍历完
// oldFiber剩下 key==="1" 未遍历
<li key="0" className="aa">0</li>
第二轮比较
  • newChildren和oldFiber都遍历完了,针对单一节点标记mutation,需要发生变化的元素记录在render阶段中,加update更新

  • newChildren剩下了,oldFiber没剩下,意味着新增了
    剩余的reset newChildren直接添加到workInProgress Fiber
    新增元素打标,将mutation记为 Placement

  • newChildren 没剩下 oldFiber 剩下了,意味着删除了
    workInProgress Fiber中将旧的循环遍历掉,标记mutationDELETION

  • newChildren和oldFiber都剩下了
    第二轮遍历:
    const exisitingChildren = map(),通过map.get获取oldFiber剩下的所有存在existingChildren中

    • key:oldFiber中的key
    • value:oldFiber中的value

    遍历 newChildren 剩下的
    exisitingChildren.has(newChildren[i].key),有的话获取这个元素,然后删除掉
    lastPlacedIndex:最后一个可复用的节点,再当前oldFiber的索引位置

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Update 状态更新

触发状态更新的方式

  • 初始化的render
  • setState
  • useState
  • forceUpdate

进行状态更新时候会创建一个Update 对象存储变更相关联的内容
包含到对应的fiber,在beginWork的过程中,找到这个fiber,记录到当前更新的数组队列updateQueue

Update是在render beginWork找到要更新的元素,Update置入要更新的节点中,去触发它的state

// 一次更新的内容
const update: Update<*> = {eventTime, //获取当前的执行时间,判断执行更新的耗时lane, //优先级任务suspenseConfig, tag: UpdateState, //包裹住更新的状态,updateState | ReplaceState | forceUpdatepayload: null, //不同的更新元素带有的参数是什么callback: null,next: null, // 也是一个链表,下一次更新的内容
};
//一次更新的 update1 next->update2 next->update3 
//div1 div2

fiber 节点 updateQueue指代的是div1,div2的变化

//一个更新的fiber的节点
const queue: UpdateQueue<State> = {baseState: fiber.memoizedState, //fiber中本身的statefirstBaseUpdate: null, //第一个更新的lastBaseUpdate: null, //最后一个更新的shared: {pending: null, //关联链表的方式,第一个更新,第二个更新,...最后一个更新,通过这个来关联的},effects: null,};

在这里插入图片描述
在这里插入图片描述
pending候车的上车
先上u3
在这里插入图片描述

在这里插入图片描述
这都是在render中做的,进入到commit阶段后,不管是谁,都不能被中断了,因为都已经在视图中了
因此,说的 异步可中断,说的都是在内存中能够做到的事情

u2优先级高于u1
但是链表:u1 — u2 这个顺序始终不会变
也就意味着,由于优先级,先执行的是u2,然后再执行一次 u1 和 u2。由此,高优先级的任务可能会触发两次

Concurrent Mode

协调模式

  • Fiber 异步可中断
  • Scheduler 协调器,结合可分片,异步可中断
  • lane 优先级
  1. 优先级不同
  2. 优先级表达的方式 batch批次执行
  3. 方便计算

划分二进制31位内容

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

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

相关文章

安装WPS后,导致python调用Excel.Application异常,解决办法

在使用xlwings编辑excel文件时&#xff0c;默认调用的是“Excel.Application”&#xff0c;如果安装过wps&#xff0c;会导致该注册表为WPS&#xff0c;会导致xlwings执行异常 因为安装过WPS&#xff0c;导致与Excel不兼容的问题&#xff0c;想必大家都听说过。有些问题及时删…

FastExcel + Java:打造高效灵活的Excel数据导入导出解决方案

作者&#xff1a;后端小肥肠 &#x1f347; 我写过的文章中的相关代码放到了gitee&#xff0c;地址&#xff1a;xfc-fdw-cloud: 公共解决方案 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 姊妹篇&#xff1a; 基于AOP的数据字典实现…

鸿蒙面试题

1.0penHarmony的系统架构是怎样的? 2.电话服务的框架? 3.OpenHarmony与HarmonyOS有啥区别?

基于 Filebeat 的日志收集

在现代分布式系统中&#xff0c;日志数据作为关键的监控与故障排查依据&#xff0c;越来越受到重视。本文将深入探讨 Filebeat 的技术原理、配置方法及在 ELK&#xff08;Elasticsearch、Logstash、Kibana&#xff09;生态系统中的应用&#xff0c;帮助开发者构建高效、稳定的日…

wireshark网络抓包

由于图片和格式解析问题&#xff0c;可前往 阅读原文 到这里已经讲了两个抓包工具的使用了&#xff0c;大家应该对抓包不是很陌生了。而wireshark相对于fiddler和charles更加偏向于网络层面的抓包或者说是一个网络封包分析工具。使用对象更适合于网络相关人员(网络管理员/相关运…

Jenkins 配置 Git Parameter 四

Jenkins 配置 Git Parameter 四 一、开启 项目参数设置 勾选 This project is parameterised 二、添加 Git Parameter 如果此处不显示 Git Parameter 说明 Jenkins 还没有安装 Git Parameter plugin 插件&#xff0c;请先安装插件 Jenkins 安装插件 三、设置基本参数 点击…

bitcoinjs学习1—P2PKH

1. 概述 在本学习笔记中&#xff0c;我们将深入探讨如何使用 bitcoinjs-lib 库构建和签名一个 P2PKH&#xff08;Pay-to-PubKey-Hash&#xff09; 比特币交易。P2PKH 是比特币网络中最常见和最基本的交易类型之一&#xff0c;理解其工作原理是掌握比特币交易构建的关键。 想要详…

2024年博客之星年度评选—创作影响力评审+主题文章创作评审目前排名(2024博客之星陪跑小分队助力2024博客之星创作者成长)

2024年博客之星年度评选—创作影响力评审主题文章创作评审目前排名 2024年博客之星主题文章创作评审文章得分公布&#xff01;2024年博客之星创作影响力评审2024年博客之星主题文章创作评审目前排名公布&#xff01; 【2024博客之星】恭喜完成✅主题创作的226位博主&#xff0…

机器学习-1:线性回归

常用的线性回归模型主要有以下这些 简单线性回归多元线性回归多项式回归岭回归套索回归弹性网络回归逐步回归 一.简单的一元线性回归 1.导入必备的库 #导入必备的库 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection …

【DuodooBMS】给PDF附件加“受控”水印的完整Python实现

给PDF附件加“受控”水印的完整Python实现 功能需求 在实际工作中&#xff0c;许多文件需要添加水印以标识其状态&#xff0c;例如“受控”“机密”等。对于PDF文件&#xff0c;添加水印不仅可以增强文件的可识别性&#xff0c;还可以防止未经授权的使用。本代码的功能需求是…

java每日精进 2.13 Ganache(区块链本地私有化部署)

需求&#xff1a;使用区块链实现数据村存储&#xff0c;记录一些不可篡改的交互信息&#xff0c;网络环境为内外网均需要部署&#xff1b; 1.准备工作&#xff08;软件安装&#xff09; 1.1 安装 Node.js 和 npm 1.2 安装 Ganache 地址如下&#xff1a;windows有可视化界面 &a…

RAGFlow和Dify对比

‌ RAGFlow和Dify都是基于大语言模型&#xff08;LLM&#xff09;的应用开发平台&#xff0c;具有相似的功能和应用场景&#xff0c;但它们在技术架构、部署要求和用户体验上存在一些差异。‌‌ RAGFlow和Dify对比 2025-02-13 22.08 RAGFlow‌ ‌技术栈‌&#xff1a;RAGFlow…

day9手机创意软件

趣味类 in:记录趣味生活&#xff08;通用&#xff09; 魔漫相机&#xff1a;真人变漫画&#xff08;通用&#xff09; 活照片&#xff1a;让照片活过来&#xff08;通用&#xff09; 画中画相机&#xff1a;与众不同的艺术 年龄检测仪&#xff1a;比一比谁更年轻&#xf…

Next.js 15【实用教程】2025最新版

官网 https://nextjs.org/docs/app/getting-started Next.js 简介 Next.js 由 Vercel 开发和维护&#xff0c;旨在解决单页应用&#xff08;SPA&#xff09;和多页应用&#xff08;MPA&#xff09;在性能和 SEO 上的不足。 核心特性 服务端渲染&#xff08;SSR&#xff09;--…

MySQL 联合索引的最左匹配原则

环境&#xff1a;MySQL 版本&#xff1a;8.0.27 执行计划基础知识 possible_keys&#xff1a;可能用到的索引 key&#xff1a;实际用到的索引 type: ref&#xff1a;当通过普通的二级索引列与常量进行等值匹配的方式 询某个表时const&#xff1a;当我们根据主键或者唯一得…

2025 西湖论剑wp

web Rank-l 打开题目环境&#xff1a; 发现一个输入框&#xff0c;看一下他是用上面语言写的 发现是python&#xff0c;很容易想到ssti 密码随便输&#xff0c;发现没有回显 但是输入其他字符会报错 确定为ssti注入 开始构造payload&#xff0c; {{(lipsum|attr(‘global…

【2024 CSDN博客之星】大学四年,我如何在CSDN实现学业与事业的“双逆袭”?

前言&#xff1a; Hello大家好&#xff0c;我是Dream。不知不觉2024年已经过去&#xff0c;自己也马上迈入23岁&#xff0c;感慨时间飞快&#xff0c;从19岁刚入大学加入CSDN&#xff0c;到现在大学毕业已经整整四年了。CSDN陪伴我走过了最青涩的四年大学时光&#xff0c;在这里…

RAG(检索增强生成)落地:基于阿里云opensearch视线智能问答机器人与企业知识库

文章目录 一、环境准备二、阿里云opensearch准备1、产品文档2、准备我们的数据3、上传文件 三、对接1、对接文本问答 一、环境准备 # 准备python环境 conda create -n opensearch conda activate opensearch# 安装必要的包 pip install alibabacloud_tea_util pip install ali…

Qt事件机制

目录 一、事件基本概念 1.1 事件基本概念和产生 1.2 事件类 1.3 事件发送 二、事件的捕获处理 2.1 事件处理流程 2.2事件分发&#xff08;event&#xff09;处捕获事件 2.3 事件过滤器 2.4 重新实现事件处理函数 三、 QEventLoop 3.1事件循环基本概念 3.2 QEventL…

20250212:sigmastar系列2-获取UUID进行授权

距离上一篇Sigmastar文章已经过去3年了。最近基于Sigmastar-330 开发人脸识别SDK,需要进行授权管理,所以需要获取UUID作为激活、授权的凭证。 本文记录2个事情:授权逻辑 + sigmastar获取UUID 1:授权流程 step1:算法SDK在设备上电,算法初始化环节,校验本地是否有加密存…