React源码解析18(8)------ 实现单节点的Diff算法

摘要

经过之前的几篇文章,我们已经实现了一个可以进行更新渲染的假React。但是如果我们把我们的jsx修改成这样:

function App() {const [age, setAge] = useState(20)const click = function() {setAge(age + 1)}return age % 2 === 0 ? jsx("div", {key: 'div1',children: jsx("span", {key: 'span',children: 'div1',onClick: click})}) : jsx("div", {key: 'div1',children: jsx("span", {key: 'span',children: 'div2',onClick: click})})
}ReactDOM.createRoot(root).render(<App />)

虽然页面的效果是正确的。但对于两个jsx,二者的区别仅仅是span标签里面的内容不同。
但是在程序里面,我们是相当于每次都重新beginWork,重新的创建Filber树,重新的创建真实DOM。
而对于这里div和span标签,它没有任何的改变,我们是否可以用一种优化策略,从而对旧资源进行利用呢? 所以Diff由此而来。

这一篇先只说单节点的Diff,因为目前还没实现带有sibling的情况。

1.修改beginWork

我们回顾一下在beginWork里面干了什么。在将jsx转换为ReactElement后,我们会通过beginWork来构建一颗Filber树。
那如果,对于可复用的FilberNode,我们是否可以不去创建,直接复用呢?
可以的,那对于React来说,什么节点是可复用的呢:

如果旧的FilberNode和新的ReactElement
key相同,type相同。
那么就是可以复用的。

所以在beginWork中的reconcileChildren方法里,如果我们发现上面的情况,我们就不会创建新的FilberNode。

function reconcileChildren(parent,element) {const newChild = diffReconcileChildren(parent, element);if(newChild) {return newChild}//其他代码。。。return filberNode
}

2.实现diffReconcileChildren方法

该方法接受两个参数,第一个是父节点,第二个是新的ReactElement。

(1)我们要先拿到父节点的child,比较child和element的key和type。
(2)将element保存在child的memoizedState里面。
(3)然后其他逻辑和reconcileChildren里的一样即可。
(4)直接返回child。

function diffReconcileChildren(filberNode, element) {const child = filberNode.child;if(child && child.key && child.type) {if(child.key === element.key && child.type === child.type) {child.memoizedProps = element;child.pendingProps = element.props;if(child.tag === HostText) {child.pendingProps = element}return child;}}
}

这里为什么要将element保存在memoizedState里面,是因为虽然节点没有改变,但是子节点可能有改变,或者属性会有改变。所以要在后面的completeWork里进行处理。

3.修改completeWork

经过上面的处理后,FilberNode不会无脑的重复创建,而是复用了。而completeWork的工作,主要是创建真实DOM,挂载在FilberNode的stateNode上。

所以在completeWork中,也不能无脑的创建真实DOM,而是也要判断是否是可复用的。

export const completeWork = (filberNode) => {const tag = filberNode.tagswitch (tag) {case HostComponent: {if(filberNode.stateNode !== null){//更新updateCompleteHostComponent(filberNode)}else{completeHostComponent(filberNode)}break;}//其他代码。。。。}
}

所以在对HostComponent的处理上,如果发现不是mount阶段,就要判断是否需要复用旧的。

4.实现updateCompleteHostComponent方法

在该方法中,我们接受filberNode。同时我们可以拿到它的memoizedState(在beginWork中传过来的)。

再判断一下key和type,如果依旧相同,那么说明是可复用的。我们直接不创建新的DOM即可。

function updateCompleteHostComponent(filberNode) {const element = filberNode.memoizedProps;if(element.key === filberNode.key && element.type === filberNode.type) {addPropsToDOM(filberNode.stateNode, filberNode.pendingProps)}else{const type = filberNode.type;const element = document.createElement(type);addPropsToDOM(element, filberNode.pendingProps)filberNode.stateNode = element;const parent = filberNode.return;if(parent && parent.stateNode && parent.tag === HostComponent) {parent.stateNode.appendChild(element)}}completeWork(filberNode.child)
}

addPropsToDOM方法是我们在实现事件机制的时候,需要调用的方法。

OK,这样我们就实现了单节点的Diff算法。

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

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

相关文章

学习红外成像仪开发注意要点

学习红外成像仪开发注意要点 三河凡科科技飞讯红外成像仪开发学习注意要点 红外成像仪是一种高级的光学设备&#xff0c;可用于探测、分析和显示红外辐射&#xff0c;它广泛应用于医学、军事、石油、矿产资源勘探等领域。红外成像仪的开发需要注意以下几个方面&#xff1a; 1…

(搜索) 剑指 Offer 12. 矩阵中的路径 ——【Leetcode每日一题】

❓剑指 Offer 12. 矩阵中的路径 难度&#xff1a;中等 给定一个 m * n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构…

使用Rust编写的一款使用遗传算法、神经网络、WASM技术的模拟生物进化的程序

模拟生物进化程序 Github地址&#xff1a;FishLife 期待各位的star✨✨✨ 本项目是一个模拟生物进化的程序&#xff0c;利用遗传算法、神经网络技术对鱼的眼睛和大脑进行模拟。该项目是使用 Rust 语言编写的&#xff0c;并编译为 WebAssembly (Wasm) 格式&#xff0c;使其可以…

QT学习方法

1 .类的学习方法 第一步:从UI文件中,找到界面的类—QMainWindow第二步:在Qt Creator工具中,找到“帮助”按钮,进入到帮助菜单界面,在选择"索引",在Look for:输入类名,找到类名,双击条目中的类名,在右侧会显示出来类的详细内容第三步:在右侧,可根据内容目录…

Spring项目使用Redis限制用户登录失败的次数以及暂时锁定用户登录权限

文章目录 背景环境代码实现0. 项目结构图&#xff08;供参考&#xff09;1. 数据库中的表&#xff08;供参考&#xff09;2. 依赖&#xff08;pom.xml&#xff09;3. 配置文件&#xff08;application.yml&#xff09;4. 配置文件&#xff08;application-dev.yml&#xff09;5…

Camera Link 接口

Camera Link是一个标准的接口协议&#xff0c;用于高速的图像数据传输&#xff0c;常被用在工业相机和图像处理系统之间。这个标准由自动视觉协会&#xff08;Automated Imaging Association&#xff0c;简称AIA&#xff09;在2000年发布&#xff0c;旨在实现各种厂家之间的高性…

在ubuntu+cpolar+rabbitMQ环境下,实现mq服务端远程访问

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

使用opencv4.7.0部署yolov5

yolov5原理和部署原理就不说了&#xff0c;想了解的可以看看这篇部署原理文章 #include <fstream> #include <sstream> #include <iostream> #include <opencv2/dnn.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp>/…

【Java转Go】快速上手学习笔记(二)之基础篇一

目录 创建项目数据类型变量常量类型转换计数器键盘交互流程控制代码运算符 创建项目 上篇我们安装好了Go环境&#xff0c;和用IDEA安装Go插件来开发Go项目&#xff1a;【Java转Go】快速上手学习笔记&#xff08;一&#xff09;之环境安装篇 。 这篇我们开始正式学习Go语言。我…

MyBatis动态SQL:打造灵活可变的数据库操作

目录 if标签trim标签where标签set标签foreach标签 动态SQL就是根据不同的条件或需求动态地生成查询语句&#xff0c;比如动态搜索条件、动态表或列名、动态排序等。 if标签 在我们填写一些信息时&#xff0c;有些信息是必填字段&#xff0c;有的则是非必填的&#xff0c;这些…

淘宝API接口的实时数据和缓存数据区别

电商API接口实时数据是指通过API接口获取到的与电商相关的实时数据。这些数据可以包括商品库存、订单状态、销售额、用户活跃度等信息。 通过电商API接口&#xff0c;可以实时获取到电商平台上的各种数据&#xff0c;这些数据可以帮助企业或开发者做出及时的决策和分析。例如&…

vue动态修改audio地址

问题&#xff1a;点击后替换url地址&#xff0c;实现了&#xff0c;但是播放器依旧没有反应。 解决&#xff1a;vue中动态替换只是替换了地址&#xff0c;并没有告诉audio标签是否要执行&#xff0c;执行什么操作。要load后才能让它知道&#xff0c;是在喊他&#xff0c;他需求…

秒懂算法 | 汉诺塔问题与木棒三角形

在数学与计算机科学中&#xff0c;递归(recursion)是指一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算&#x…

Android性能优化——线程优化

一、线程调度原理 在任意时刻&#xff0c;CPU只能执行一条指令&#xff0c;每个线程获取到CPU的使用权之后才可以执行指令也就是说在任意时刻&#xff0c;只有一个线程占用CPU 处于运行状态 多线程并发&#xff0c;实际上是指多个线程轮流获取CPU 的使用权然后分别执行各自的任…

系统安全测试要怎么做?

进行系统安全测试时&#xff0c;可以按照以下详细的步骤进行&#xff1a; 1、信息收集和分析&#xff1a; 收集系统的相关信息&#xff0c;包括架构、部署环境、使用的框架和技术等。 分析系统的安全需求、威胁模型和安全策略等文档。 2、威胁建模和风险评估&#xff1a; 使…

调用被fishhook的原函数

OC类如果通过runtime被hook了&#xff0c;可以通过逆序遍历方法列表的方式调用原方法。 那系统库的C函数被fish hook了该怎么办呢&#xff1f; 原理和OC类异曲同工&#xff0c;即通过系统函数dlopen()获取动态库&#xff0c;以动态库为参数通过系统函数dlsym()即可获取目标系统…

leetcode292. Nim 游戏(博弈论 - java)

Nim 游戏 Nim 游戏题目描述博弈论 上期经典算法 Nim 游戏 难度 - 简单 原题链接 - Nim游戏 题目描述 你和你的朋友&#xff0c;两个人一起玩 Nim 游戏&#xff1a; 桌子上有一堆石头。 你们轮流进行自己的回合&#xff0c; 你作为先手 。 每一回合&#xff0c;轮到的人拿掉 1 -…

494. 目标和

494. 目标和 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;数组回溯法动态规划 参考代码&#xff1a;数组回溯法__494目标和__动态规划 经验吸取 原题链接&#xff1a; 494. 目标和 https://leetcode.cn/problems/target-sum/description/ 完成情况&#…

Android进阶之多级列表

遇到一个需求需要显示多级列表&#xff0c;因为界面是在平板上的&#xff0c;所以层级是从左向右往下排的&#xff0c;类似于 我当时的写法是在xml布局里一个个RecyclerView往下排的 当然前提是已经规定好最大的层级我才敢如此去写界面&#xff0c;如果已经明确规定只有两级或…

69 # 强制缓存的配置

强制缓存 强制缓存&#xff1a;以后的请求都不需要访问服务器&#xff0c;状态码为 200协商缓存&#xff1a;每次都判断一下&#xff0c;告诉是否需要找缓存&#xff0c;状态码为 304 默认强制缓存&#xff0c;不缓存首页&#xff08;如果已经断网&#xff0c;那这个页面应该…