React源码解析18(10)------ 实现多节点的Diff算法
摘要
在上一篇中,实现了多节点的渲染。但是之前写得diff算法,只能适用于单节点的情况,例如这种情况:
<div><p><span></span></p>
</div>
如果对于多节点的情况:
<ul><li></li><li></li><li></li>
</ul>
之前实现的diff算法就不会有效果了,所以在这一篇中,我们主要实现针对于多节点的diff算法。
实现之前,我们先将index.js修改一下:
function App() {const [num, setNum] = useState(100)const click1 = () => {console.log(num);setNum(num + 1)}return num % 2 > 0 ? jsx("ul", {onClick: click1,key: 'ul',children: [jsx("li", {children: "1",key: "1"}), jsx("li", {children: "2",key: "2"}), jsx("li", {children: "3",key: "3"})]}): jsx("ul", {onClick: click1,key: 'ul',children: [jsx("li", {children: "2",key: "2"}), jsx("li", {children: "1",key: "1"}), jsx("li", {children: "3",key: "3"})]});
}ReactDOM.createRoot(root).render(<App />)
1.修改beginWork流程
在reconcileChildren方法里面,我们判断了如果element为数组的情况,就是多节点。所以我们需要在这里进行diff算法的处理。
function reconcileChildren(parent,element) {//其他代码。。。。}else if(Array.isArray(element) && element.length > 0) {const newChild = diffReconcileManyChildren(parent, element);if(newChild) {return newChild}//其他代码。。。。
所以我们的diff算法那主要是在diffReconcileManyChildren方法里面实现。
对于多节点的Diff,我们需要进行以下步骤。
- 创建变量lastIndex,用来标记索引
- 将旧的filberNode列表,转换为map结构,key为filberNode的key,value为filberNode
- 遍历新的element数组。
- 如果element.key可以在map中找到,lastIndex记录为找到的filberNode的index
- 如果找不到,创建新的FilberNode
- 继续遍历,如果又在map中找到filberNode,比较fiberNode的index和lastIndex.
- 如果index < lastIndex,给filberNode打上移动的标志
基于上面的步骤,实现diffReconcileManyChildren方法
function diffReconcileManyChildren(filberNode, element) {let firstChild = filberNode.child;if(!firstChild) {return;}const head = {sibling: null};const oldChildren = []while(firstChild) {oldChildren.push(firstChild);firstChild = firstChild.sibling;}const oldMap = new Map();oldChildren.forEach((item,index) => {item.index = indexif(item.key) {oldMap.set(item.key, item)}else{oldMap.set(index, item)}})let lastIndex = 0;let empty = headfor(let i=0; i<element.length; i++) {if(!element[i].key){continue;}const useFilber = oldMap.get(element[i].key);useFilber.sibling = null;if(useFilber) {if(useFilber.index < lastIndex) {useFilber.flags = 'insert'}useFilber.memoizedProps = element[i]lastIndex = useFilber.index;empty.sibling = useFilber;empty = empty.sibling;oldMap.delete(element[i].key)}else{const filberNode = new FilberNode(HostComponent, element[i].props, element[i].key) filberNode.type = element[i].typeempty.sibling = filberNode;empty = empty.sibling;}}return head.sibling;
}
经过上面的处理,beginWork流程结束,可复用的filberNode就不会重复创建。
2.修改completeWork流程
在beginWork中,可复用的节点已经被打上了insert的标志,所以在updateCompleteHsotComponent中,我们要判断是不是insert的标志,如果是,就不能无脑创建,而是通过移动DOM的位置来复用DOM。
同时,也要对同级的sibling进行递归处理。
function updateCompleteHostComponent(filberNode) {//其他代码。。。。if(element.key === filberNode.key && element.type === filberNode.type) {addPropsToDOM(filberNode.stateNode, filberNode.pendingProps);if(filberNode.flags === 'insert') {const parent = filberNode.return;parent.stateNode.insertBefore(filberNode.stateNode, filberNode.sibling?.stateNode)}//其他代码if(filberNode.sibling) {completeWork(filberNode.sibling)}
}
在对HostText的处理中,也要考虑,当前的操作是更新还是替换。
function completeHostText(filberNode) {//其他代码。。。。。if(parent && parent.stateNode && parent.tag === HostComponent) {if(!parent.stateNode) {parent.stateNode.appendChild(element);}else{parent.stateNode.replaceChildren(element);}}//其他代码。。。。
}
相关文章:
React源码解析18(10)------ 实现多节点的Diff算法
摘要 在上一篇中,实现了多节点的渲染。但是之前写得diff算法,只能适用于单节点的情况,例如这种情况: <div><p><span></span></p> </div>如果对于多节点的情况: <ul><…...
Linux指令篇!
Linux 是一个广泛使用的开源操作系统,以下是一些常用的 Linux 知识点和指令: 1. 文件和目录操作: - ls:列出目录内容 - cd:切换目录 - pwd:显示当前工作目录 - mkdir:创建目录 - touch…...
Vue2学习笔记のVue组件化编程
目录 Vue组件化编程非单文件组件基本使用几个注意点组件的嵌套VueComponent一个重要的内置关系 单文件组件index.htmlmain.jsApp.vueSchool.vueStudent.vue 各位小伙伴们好呀,不知道上一篇文章你是否有收获!这篇是Vue2学习笔记第二篇,也是Vue…...
跨境电商儿童沙画办理EN71测试标准
儿童沙画就是小孩子玩的那种用彩色沙子或者彩色墨水,在有图形轮廓的纸片上去绘画,可以按照儿童沙画底板上的人物轮廓线条,动物线条,风景线条,动漫线条,去添加自己喜欢的颜色,让单调的线条变成自…...
chrome浏览器账号密码输入框自动填充时背景色不变
处理前 使用延时的方式解决 .login-box input,password:-webkit-autofill .login-box input,password:-webkit-autofill:hover, .login-box input,password:-webkit-autofill:focus, .login-box input,password:-webkit-autofill:active {-webkit-transition-delay: 999999…...
【ARM】Day9 cortex-A7核I2C实验(采集温湿度)
1. 2、编写IIC协议,采集温湿度值 iic.h #ifndef __IIC_H__ #define __IIC_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" #include "led.h" /* 通过程序模拟实现I2C总线的时序和协议* GPIOF ---> AHB4* I2C1_S…...
【Leetcode】移动零
移动零 题目描述算法描述编程代码 链接: 移动零 题目描述 算法描述 编程代码 class Solution { public:void moveZeroes(vector<int>& nums) {//题目要求不可以复制数组,开辟额外空间int dest -1,curr 0;for(;curr < nums.size();curr){if(nums[cu…...
数据结构入门 — 顺序表详解
前言 数据结构入门 — 顺序表详解 博客主页链接:https://blog.csdn.net/m0_74014525 关注博主,后期持续更新系列文章 文章末尾有源码 *****感谢观看,希望对你有所帮助***** 文章目录 前言一、顺序表1. 顺序表是什么2. 优缺点 二、概念及结构…...
SimpleCG绘图函数(9)--绘制各种线条
一、所有线段函数概述 可填充图形绘制函数都介绍完了,还有一些特殊线条的绘制将在本篇进行讲解。所有特殊线条函数如下所示,其中还有一个区域填充函数floodfill比较特殊,是配合线条函数使用的: //绘制一系列折线段 //折线段以一组…...
RISCV 6 RISC-V加载存储指令
RISCV 6 RISC-V加载存储指令 1 RV32I Load and Store Instructions1.1 LOAD instructions1.1.1 加载指令的指令格式1.1.2 加载指令在使用时需要注意的点 1.2 STORE instructions1.2.1 存储指令的指令格式1.2.2 存储指令在使用时需要注意的点 2 RV64 Load and Store Instruction…...
木叶飞舞之【机器人ROS2】篇章_第二节、turtlebot3安装
没有真实小车的情况下,利用gazebo的仿真,操作小乌龟来学习ros2。废话不多说,直接上命令。 Install Gazebo sudo apt install ros-humble-gazebo-*Install Cartographer 假如前一节未安装源码版本的cartographer,那就安装apt版本…...
【论文阅读】自动驾驶安全的研究现状与挑战
文章目录 摘要1.引言1.1.自动驾驶安全1.2.攻击面1.3.内容和路线图 2.自动驾驶技术2.1.组成2.2.技术 3.传感器安全3.1.照相机3.2.GNSS(全球导航系统)/IMU(惯性测量单元)3.3.超声波传感器3.4.毫米波雷达3.5.激光雷达3.6.多传感器交叉…...
标签打印小工具 选择图片打印,按实际尺寸打印。可旋转图片
您可以尝试使用以下标签打印工具: 柯尼卡美能达标签打印机:功能齐全、易于使用的打印机,支持各种标签尺寸和类型。 赛门铁克标签打印机:高速打印、可靠性强的打印机,支持多种操作系统和软件。 齐柏林标签打印机&…...
什么是深拷贝和浅拷贝?
面试回答 在计算机内存中,每个对象都有一个地址,这个地址指向对象在内存中存储的位置。当我们使用变量引用一个对象时,实际上是将该对象的地址赋值给变量。因此,如果我们将一个对象复制到另一个变量中国,实际上是将对象…...
安装docker服务及docker基本操作
一、docker安装(yum安装) 基于centos7 1.添加docker-ce 源信息 安装依赖包(yum-utils 提供了 yum-config-manager ,并且 device mapper 存储驱动程序需要device-mapper-persistent-data 和 lvm2) yum install yum-…...
【项目经验】:项目中下拉框数据太多造成页面卡顿(二)
一.项目需求 下拉框下拉列表数据是由后端返回的,而且他会变化,所以数据不是写死的而且数据量大。上一篇博客http://t.csdn.cn/sSNTa我们是用的数据懒加载的方式,这次我们使用远程搜索的方式解决这个问题。 二.用到的组件方法介绍 filterabl…...
Prompt本质解密及Evaluation实战(一)
一、基于evaluation的prompt使用解析 基于大模型的应用评估与传统应用程序的评估不太一样,特别是基于GPT系列或者生成式语言模型,因为模型生成的内容与传统意义上所说的内容或者标签不太一样。 以下是借用了ChatGPT官方的evaluation指南提出的对结果的具…...
linux 在系统已有python2版本下安装python3
方法一:使用包管理器安装 更新包索引: sudo apt update 安装Python3: sudo apt install python3 安装Python3的pip(如果你需要): sudo apt install python3-pip 验证Python 2和3的安装: pyt…...
IO多路转接 ——— select、poll、epoll
select初识 select是系统提供的一个多路转接接口。 select系统调用可以让我们的程序同时监视多个文件描述符的上的事件是否就绪。 select的核心工作就是等,当监视的多个文件描述符中有一个或多个事件就绪时,select才会成功返回并将对应文件描述符的就绪…...
FPGA原理与结构——FIFO IP核原理学习
一、FIFO概述 1、FIFO的定义 FIFO是英文First-In-First-Out的缩写,是一种先入先出的数据缓冲器,与一般的存储器的区别在于没有地址线, 使用起来简单,缺点是只能顺序读写数据,其数据地址由内部读写指针自动加1完成&…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
