react中的diff算法

diff算法
对于React团队发现在日常开发中对于更新组件的频率,会比新增和删除的频率更高,所以在diff算法里,判断更新的优先级会更高。对于Vue2的diff算法使用了双指针,React的diff算法没有使用双指针,是因为更新的jsx对象的newChildren为数组的形式,但是和newChildren中每个组件比较的是current fiber,对fiber的兄弟节点是通过silbing来相连的,我们通过下标来去获取下一个newChildren项,但是对于fiber只能通过fiber.silbing来获取对应的项,所以没有使用双指针法来进行diff。

所以React的diff算法的整体逻辑会经历两轮的遍历。

第一轮遍历:
会尝试逐个的复用节点;

第二轮遍历:
处理上一轮遍历中没有处理完的节点。

一、第一轮遍历:
从前往后以此进行遍历,存在三种情况:

  • 若新旧子节点的key和type都相同,则说明可以复用;

  • 若新旧子节点的key相同,但是type不同,这个时候会根据

    reactElement来生成一个全新的fiber,旧的fiber被放入到deletions数组中,回头统一删除,但是注意,此时遍历不回停止;

  • 若新旧子节点的key和type都不相同,则结束遍历。

实例1:
前:

<div>
<div key='a'>a</div>
<div key='b'>b</div>	
<div key='c'>c</div>	
<div key='d'>d</div>	
</div>	

后:

<div>
<div key='a'>a</div>
<div key='b'>d</div>	
<div key='e'>e</div>	
<div key='d'>d</div>	
</div>	

我们发现div.key.a和我们发现div.key.b可以复用,继续往后走
走到div.key.e,我们发现key不同,结束第一轮遍历;

实例2:

<div>
<div key='a'>a</div>
<div key='b'>b</div>	
<div key='c'>c</div>	
<div key='d'>d</div>	
</div>	

更新后:

<div>
<div key='a'>a</div>
<div key='b'>b</div>	
<p key='c'>c</p>	
<div key='d'>d</div>	
</div>	

前面div.keya和div.keyb都会复用,接下来到了第3个节点,我们发现key是相同的,但是type不同,就会将对应的旧的fiberNode放到一个叫deletions中数组中,回头统一删除,然后根据新的react元素创建一个新的FiberNode,但此时的遍历不会结束。
接下来往后面继续遍历,遍历什么时候结束?
到末尾了,也就是遍历完了
或者是和实例1相同,发现key不同。

二、第二轮遍历:
如果第一轮遍历被提前停止了,那么意味着有新的React元素或者旧的FiberNode没有遍历完,此时就会采用第二轮遍历;
第二轮遍历会处理这么三种情况:
只剩下旧子节点:将旧的子节点放到deletions数组里面直接删除掉(删除的情况);
只剩下新的jsx元素:根据RecreatElement元素来创建新的FiberNode节点(新增的情况);
新旧节点都有剩余:
会将剩余的FiberNode节点放到一个map里面,遍历剩余的jsx元素,然后从map中找出可以复用的fiberNode,若能找到就拿来复用(移动的情况)
若不能找到,就新增,然后若剩余的jsx元素都遍历完了,map结构中还有剩余的fiber节点,就将这些fiber节点添加到deletions数组中,之后做统一删除。

例子:

// 之前
abcd// 之后
acdb===第一轮遍历开始===
a(之后)vs a(之前)  
key不变,可复用
此时 a 对应的oldFiber(之前的a)在之前的数组(abcd)中索引为0
所以 lastPlacedIndex = 0;继续第一轮遍历...c(之后)vs b(之前)  
key改变,不能复用,跳出第一轮遍历
此时 lastPlacedIndex === 0;
===第一轮遍历结束======第二轮遍历开始===
newChildren === cdb,没用完,不需要执行删除旧节点
oldFiber === bcd,没用完,不需要执行插入新节点将剩余oldFiber(bcd)保存为map// 当前oldFiber:bcd
// 当前newChildren:cdb继续遍历剩余newChildrenkey === c 在 oldFiber中存在
const oldIndex = c(之前).index;
此时 oldIndex === 2;  // 之前节点为 abcd,所以c.index === 2
比较 oldIndex 与 lastPlacedIndex;如果 oldIndex >= lastPlacedIndex 代表该可复用节点不需要移动
并将 lastPlacedIndex = oldIndex;
如果 oldIndex < lastplacedIndex 该可复用节点之前插入的位置索引小于这次更新需要插入的位置索引,代表该节点需要向右移动在例子中,oldIndex 2 > lastPlacedIndex 0,
则 lastPlacedIndex = 2;
c节点位置不变继续遍历剩余newChildren// 当前oldFiber:bd
// 当前newChildren:dbkey === d 在 oldFiber中存在
const oldIndex = d(之前).index;
oldIndex 3 > lastPlacedIndex 2 // 之前节点为 abcd,所以d.index === 3
则 lastPlacedIndex = 3;
d节点位置不变继续遍历剩余newChildren// 当前oldFiber:b
// 当前newChildren:bkey === b 在 oldFiber中存在
const oldIndex = b(之前).index;
oldIndex 1 < lastPlacedIndex 3 // 之前节点为 abcd,所以b.index === 1
则 b节点需要向右移动
===第二轮遍历结束===最终acd 3个节点都没有移动,b节点被标记为移动

再看个例子:


// 之前
abcd
// 之后
dabc
===第一轮遍历开始===
d(之后)vs a(之前)
key不变,type改变,不能复用,跳出遍历
===第一轮遍历结束===
===第二轮遍历开始===
newChildren === dabc,没用完,不需要执行删除旧节点
oldFiber === abcd,没用完,不需要执行插入新节点
将剩余oldFiber(abcd)保存为map
继续遍历剩余newChildren
// 当前oldFiber:abcd
// 当前newChildren dabc
key === d 在 oldFiber中存在
const oldIndex = d(之前).index;
此时 oldIndex === 3; // 之前节点为 abcd,所以d.index === 3
比较 oldIndex 与 lastPlacedIndex;
oldIndex 3 > lastPlacedIndex 0
则 lastPlacedIndex = 3;
d节点位置不变
继续遍历剩余newChildren
// 当前oldFiber:abc
// 当前newChildren abc
key === a 在 oldFiber中存在
const oldIndex = a(之前).index; // 之前节点为 abcd,所以a.index === 0
此时 oldIndex === 0;
比较 oldIndex 与 lastPlacedIndex;
oldIndex 0 < lastPlacedIndex 3
则 a节点需要向右移动
继续遍历剩余newChildren
// 当前oldFiber:bc
// 当前newChildren bc
key === b 在 oldFiber中存在
const oldIndex = b(之前).index; // 之前节点为 abcd,所以b.index === 1
此时 oldIndex === 1;
比较 oldIndex 与 lastPlacedIndex;
oldIndex 1 < lastPlacedIndex 3
则 b节点需要向右移动
继续遍历剩余newChildren
// 当前oldFiber:c
// 当前newChildren c
key === c 在 oldFiber中存在
const oldIndex = c(之前).index; // 之前节点为 abcd,所以c.index === 2
此时 oldIndex === 2;
比较 oldIndex 与 lastPlacedIndex;
oldIndex 2 < lastPlacedIndex 3
则 c节点需要向右移动
===第二轮遍历结束===

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

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

相关文章

第三十天| 51. N皇后

Leetcode 51. N皇后 题目链接&#xff1a;51 N皇后 题干&#xff1a;按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整…

Windows10安装VScode + mingw64 + GSL

这里写自定义目录标题 安装VScode安装 MingW64测试C/C环境 安装msys安装GSL配置VScodelaunch.json创建tasks.json配置c_cpp_properties.json 测试验证其他可参考 安装VScode 在官网下载VScode:VScode按导引步骤安装VScode&#xff0c;并选择自己想安装的位置创建自己的工作区&…

three.js 箭头ArrowHelper的实践应用

效果&#xff1a; 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div></div></el-main></…

【芯片设计- RTL 数字逻辑设计入门 15 -- 函数实现数据大小端转换】

文章目录 函数实现数据大小端转换函数语法函数使用的规则Verilog and Testbench综合图VCS 仿真波形 函数实现数据大小端转换 在数字芯片设计中&#xff0c;经常把实现特定功能的模块编写成函数&#xff0c;在需要的时候再在主模块中调用&#xff0c;以提高代码的复用性和提高设…

Qt PCL学习(文章链接汇总)

Qt PCL学习&#xff08;一&#xff09;&#xff1a;环境搭建 Qt PCL学习&#xff08;二&#xff09;&#xff1a;点云读取与保存 Qt PCL学习&#xff08;三&#xff09;&#xff1a;点云滤波 持续更新中…

Unity3d Shader篇(六)— BlinnPhong高光反射着色器

文章目录 前言一、BlinnPhong高光反射着色器是什么&#xff1f;1. BlinnPhong高光反射着色器的工作原理2. BlinnPhong高光反射着色器的优缺点优点缺点 3. 公式 二、使用步骤1. Shader 属性定义2. SubShader 设置3. 渲染 Pass4. 定义结构体和顶点着色器函数5. 片元着色器函数 三…

图文并茂讲解Travelling Salesman

题目 思路 一道lca板子题&#xff0c;不会的同学可以先康康 详解最近公共祖先(LCA)-CSDN博客 我们可以发现&#xff0c;商人是从1开始&#xff0c;旅行到第一个城镇&#xff0c;再到第二个&#xff0c;第三个…… 那么我们只需要求出1~第一个城镇的距离&#xff0c;第一个城…

go语言实现LRU缓存

go语言实现LRU Cache 题目描述详细代码 题目描述 设计和构建一个“最近最少使用”缓存&#xff0c;该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值)&#xff0c;并在初始化时指定最大容量。当缓存被填满时&#xff0c;它应该删除最近最…

Elasticsearch(ES) 简述请求操作索引下文档 增删查改操作

上文 Elasticsearch(ES) 创建带有分词器规则的索引 带着大家创建了一个带有分词功能的索引 老规矩 我们启动一下ES服务 本文 我们就来说说 关于文档的操作 我们先来添加一个文档 就像数据库加一条数据一样 这里 并不需要指定什么表结构和数据结构 它的文档结构是无模式的 添…

为什么有人不推荐选择网络安全行业?

很多人是这样了解网络安全专业的&#xff1a; 专业就业面窄&#xff0c;市场需求量小。网络空间安全主要是从事网络安全方面的工作&#xff0c;就业面窄。它和软件工程、计算机科学与技术等专业不一样&#xff0c;后者毕业可以从事软件开发或者大数据等方面的工作。而网络安全就…

Redis——缓存的持久化

1、持久化机制 Redis的所有数据都保存在内存中&#xff0c;如果没有配置持久化功能&#xff0c;Redis重启后数据就会全部丢失&#xff0c;所以需要开启Redis的持久化功能&#xff0c;将数据保存到磁盘上&#xff0c;这样当Redis重启后&#xff0c;可以从磁盘中恢复数据。Redis…

【JS逆向六】(下)逆向解密某网站返回的密文的值 仅供学习

逆向日期&#xff1a;2024.02.07 使用工具&#xff1a;Node.js 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 可使用AES进行解密处理&#xff08;直接解密即可&#xff09;&#xff1a;在线AES加解密工具 1、打开某某网站(请使用文章开头的…

使用深度学习对视频进行分类

目录 加载预训练卷积网络 加载数据 将帧转换为特征向量 准备训练数据 创建 LSTM 网络 指定训练选项 训练 LSTM 网络 组合视频分类网络 使用新数据进行分类 辅助函数 此示例说明如何通过将预训练图像分类模型和 LSTM 网络相结合来创建视频分类网络。 要为视频…

Android:自定义控件

3.16自定义控件 1.自定义VIEW组件 创建CustomizeView,继承View。重写onDraw方法,通过onDraw方法绘制我们自定义的图像、位图、路径等。 示例: //自定义绘制View public class CutomizeView extends View {//构造方法public CutomizeView(Context context) {super(context…

【原创 附源码】Flutter海外登录--Tiktok登录最详细流程

最近接触了几个海外登录的平台&#xff0c;踩了很多坑&#xff0c;也总结了很多东西&#xff0c;决定记录下来给路过的兄弟坐个参考&#xff0c;也留着以后留着回顾。更新时间为2024年2月7日&#xff0c;后续集成方式可能会有变动&#xff0c;所以目前的集成流程仅供参考&#…

[BeginCTF]真龙之力

安装程序 双击安装 出现了安装失败的标签&#xff0c;开发者不允许测试。 查看Mainfest入口文件 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android" android:versionCo…

71_Pandas.DataFrame排名

71_Pandas.DataFrame排名 使用rank()方法对pandas.DataFrame和pandas.Series的行/列进行排名。 sort_values() 是一种按升序或降序对 pandas.DataFrame 列和 pandas.Series 进行排序的方法&#xff0c;但rank() 返回每个元素的排名而不对数据进行排序。 请参阅下面的文章了解…

[vscode]ssh报错: Resolver error: Error: XHR failedscode错误

场景问题&#xff1a;通过vscode ssh连接远程服务器失败&#xff0c;报错&#xff1a;Resolver error: Error: XHR failedscode&#xff1a; 问题原因&#xff1a;~/.vscode-server/bin/一串数字下的vscode-server-linux-x64.tar.gz由于某种原因无法正常下载 解决方式&#x…

JavaScript实现轮播图方法

效果图 先来看下效果图&#xff0c;嫌麻烦就不用具体图片来实现了&#xff0c;主要是理清思路。&#xff08;自动轮播&#xff0c;左右按钮切换图片&#xff0c;小圆点切换图片&#xff0c;鼠标移入暂停轮播&#xff0c;鼠标移出继续轮播&#xff09; HTML 首先是html内容&am…

【QT学习十四】 文件目录操作

目录 一、概述 二、详解 1. QFile QFile 类中的一些静态方法&#xff1a; 使用示例&#xff1a; 注意事项&#xff1a; 2. QDir 成员函数 使用实例&#xff1a; 注意事项&#xff1a; 3. QFileInfo 成员函数 使用实例 4. QTemporaryFile 成员函数 使用实例 注…