渲染器——双端Diff算法

简单 Diff 算法利用虚拟节点的 key 属性,尽可能地复用 DOM 元素,并通过移动 DOM 的方式来完成更新,从而减少不断地创建和销毁DOM 元素带来的性能开销。但是,简单 Diff 算法仍然存在很多缺陷,这些缺陷可以通过双端 Diff 算法解决。

1、双端比较的原理

简单 Diff 算法的问题在于,它对 DOM 的移动操作并不是最优的。我们拿上一章的例子来看,如下图所示:
在这里插入图片描述
在这个例子中,如果使用简单 Diff 算法来更新它,则会发生两次 DOM 移动操作,如下图所示:
在这里插入图片描述
第一次 DOM 移动操作会将真实 DOM 节点 p-1 移动到真实DOM 节点 p-3 后面。第二次移动操作会将真实 DOM 节点 p-2 移动到真实 DOM 节点 p-1 后面。最终,真实 DOM 节点的顺序与新的一组子节点顺序一致:p-3、p-1、p-2。

然而,上述更新过程并非最优解。在这个例子中,其实只需要通过一步 DOM 节点的移动操作即可完成更新,即只需要把真实 DOM 节点 p-3 移动到真实 DOM 节点 p-1 前面,如下图所示:
在这里插入图片描述
可以看到,理论上只需要一次 DOM 移动操作即可完成更新。但简单 Diff 算法做不到这一点,不过双端Diff 算法可以做到。接下来,我们就来讨论双端 Diff 算法的原理。

顾名思义,双端 Diff 算法是一种同时对新旧两组子节点的两个端点进行比较的算法。因此,我们需要四个索引值,分别指向新旧两组子节点的端点,如下图所示:
在这里插入图片描述
用代码来表达四个端点,如下面 patchChildren 和patchKeyedChildren 函数的代码所示:

01 function patchChildren(n1, n2, container) {
02   if (typeof n2.children === 'string') {
03     // 省略部分代码
04   } else if (Array.isArray(n2.children)) {
05     // 封装 patchKeyedChildren 函数处理两组子节点
06     patchKeyedChildren(n1, n2, container)
07   } else {
08     // 省略部分代码
09   }
10 }
11
12 function patchKeyedChildren(n1, n2, container) {
13   const oldChildren = n1.children
14   const newChildren = n2.children
15   // 四个索引值
16   let oldStartIdx = 0
17   let oldEndIdx = oldChildren.length - 1
18   let newStartIdx = 0
19   let newEndIdx = newChildren.length - 1
20 }

在上面这段代码中,我们将两组子节点的打补丁工作封装到了patchKeyedChildren 函数中。在该函数内,首先获取新旧两组子节点 oldChildren 和 newChildren,接着创建四个索引值,分别指向新旧两组子节点的头和尾,即 oldStartIdx、oldEndIdx、newStartIdx 和 newEndIdx。有了索引后,就可以找到它所指向的虚拟节点了,如下面的代码所示:

01 function patchKeyedChildren(n1, n2, container) {
02   const oldChildren = n1.children
03   const newChildren = n2.children
04   // 四个索引值
05   let oldStartIdx = 0
06   let oldEndIdx = oldChildren.length - 1
07   let newStartIdx = 0
08   let newEndIdx = newChildren.length - 1
09   // 四个索引指向的 vnode 节点
10   let oldStartVNode = oldChildren[oldStartIdx]
11   let oldEndVNode = oldChildren[oldEndIdx]
12   let newStartVNode = newChildren[newStartIdx]
13   let newEndVNode = newChildren[newEndIdx]
14 }

其中,oldStartVNode 和 oldEndVNode 是旧的一组子节点中的第一个节点和最后一个节点,newStartVNode 和newEndVNode 则是新的一组子节点的第一个节点和最后一个节点。有了这些信息之后,我们就可以开始进行双端比较了。怎么比较呢?如下图所示:
在这里插入图片描述
在双端比较中,每一轮比较都分为四个步骤,如上图中的连线所示:

  • 第一步:比较旧的一组子节点中的第一个子节点 p-1 与新的一组子节点中的第一个子节点 p-4,看看它们是否相同。由于两者的 key 值不同,因此不相同,不可复用,于是什么都不做。

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

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

相关文章

【阿里云】图像识别 摄像模块 语音模块

USB 摄像头模块测试及配置 一、首先将 USB 摄像头插入到 Orange Pi 开发板的 USB 接口中二、然后通过 lsmod 命令可以看到内核自动加载了下面的模块三、通过 v4l2-ctl 命令可以看到 USB 摄像头的设备节点信息为 /dev/video0四、使用 fswebcam 测试 USB 摄像头五、使用 motion …

chrome内置路径合集

设置黑夜模式: 输入网址:chrome://flags/ 搜索dark 改为enable 实验项目路径 chrome://flags/ 可用来启用或者关闭某些 Chrome 的实验功能 chrome://settings 将快速打开 Chrome 浏览器的设置页面,页面的内容分类划分为基础和高级设置选项 …

以太网_底层

【实物图】 【网线接口】 MAC(媒体访问控制器):控制数据的收发和管理,和用户层打交到;通过MII/RMII、SMI接口和PHY进行通信。 PHY(以太网物理层收发器):中间体,负责收发信号的转换 常见PHY芯片有:LAN8720…

PIL如何批量给图片添加文字水印?

PIL如何批量给图片添加文字水印? 1 简单引入2 关于PIL3 本文涉及的PIL的几个类4 实现原理5 实现过程5.1 原始图片5.2 导入相关模块5.3 初始化数据5.4 水印字体设置5.5 打开原始图片并新建存储对象5.6 计算图片和水印的大小5.7 选择性设置水印文字5.8 绘制文字并设置…

基于yolov8的车牌检测训练全流程

YOLOv8 是Ultralytics的YOLO的最新版本。作为一种前沿、最先进(SOTA)的模型,YOLOv8在之前版本的成功基础上引入了新功能和改进,以提高性能、灵活性和效率。YOLOv8支持全范围的视觉AI任务,包括检测, 分割, 姿态估计, 跟踪, 和分类。这种多功能…

Java项目实战《苍穹外卖》 三、登录功能

测测你是什么人格吧,地址: MBTI 16种人格测试官网 系列文章目录 苍穹外卖是黑马程序员2023年的Java实战项目,作为业余练手用,需要源码或者课程的可以找我,无偿分享 Java项目实战《苍穹外卖》 一、项目概述Java项目实战…

Redis从入门到精通(三)-高阶篇

文章目录 0. 前言[【高阶篇】3.1 Redis协议(RESP )详解](https://blog.csdn.net/wangshuai6707/article/details/132742584)[【高阶篇】3.3 Redis之底层数据结构简单动态字符串(SDS)详解](https://blog.csdn.net/wangshuai6707/article/details/131101404)[【高阶篇】3.4 Redis…

Java实现堆算法

堆是一种特殊的数据结构,它是一棵完全二叉树,且满足堆的性质:对于每个节点,它的值都不小于(或不大于)它的孩子节点的值。根节点的值就是堆中的最大值(或最小值)。 Java中提供了一个…

自然语言处理:Transformer与GPT

Transformer和GPT(Generative Pre-trained Transformer)是深度学习和自然语言处理(NLP)领域的两个重要概念,它们之间存在密切的关系但也有明显的不同。 1 基本概念 1.1 Transformer基本概念 Transformer是一种深度学…

HarmonyOS基础组件之Button三种类型的使用

简介 HarmonyOS在明年将正式不再兼容Android原生功能,这意味着对于客户端的小伙伴不得不开始学习HarmonyOS开发语言。本篇文章主要介绍鸿蒙中的Button使用。 HarmonyOS中的Button相较于Android原生来说,功能比较丰富,扩展性高,减…

mysql binlog

binlog日志介绍 什么是 binlog binlog是server层共有的,是记录的数据更新历史,主要用来做主从同步和数据的实时备份。 binlog 怎么开启 mysql的配置文件添加相关配置并重启mysql # 1. linux打开mysql配置文件 vi /etc/my.cnf# 2. 添加binlog配置 [mysql…

STC单片机选择外部晶振烧录程序无法切换回内部晶振导致单片机不能使用

STC单片机选择外部晶振烧录程序无法切换回内部晶振导致单片机不能使用 1.概述 在学习51单片机过程中,选择了STC的12C2052AD型号单片机作为入门芯片。前几个课题实验使用默认的内部晶振烧录程序,运行都没有问题。 选择一个LED亮度渐变的课题做实验&…

nvm管理node版本过程记录

写在前面 今天记录一下windows电脑安装nvm同时使用nvm管理node版本的,为什么写windows版本的呢?因为mac版本的基本上是不需要进行记录的,相对windows的安装是简单很多的,行了废话不多说,我们直接开始 安装nvm nvm下载…

redis运维(十二)

一 位图 ① 概念 1、说明:位图还是在操作字符串2、位图玩字符串在内存中存储的二进制3、ASCII字符通过映射转化为二进制4、操作的是字符串value ② ASCII字符铺垫 1、控制ASCII字符 2、ASCII可显示字符 ③ SETBIT 细节: setbit 命令的返回值是之…

C++ 多态和虚函数详解

本文章内容来源于C课堂上的听课笔记 多态基础 多态(Polymorphism)是面向对象编程中的一个重要概念,它允许使用统一的接口来表示不同的对象和操作。多态性有两种主要形式:静态多态性(编译时多态性)和动态多…

华为无线ac+fit三层组网,每个ap发射不同的业务vlan

ap管理dhcp在ac控制器上,业务dhcp在汇聚上 配置WLAN业务 (1)配置VAP模板 • 配置员工网络的VAP模板(employee) [AC-wlan-view] security-profile name employee //创建名为“employee”的安全模板 [AC-wlan-sec-prof-…

Git面经

Git八股文 第一章 git基础 1.1 什么是git git是一款免费的开源的分布式版本控制系统 1.2 为什么要使用git 为了保留之前的所有版本,方便回滚或修改 1.3 集中化版本控制系统和分布式版本控制系统的区别 集中化版本控制系统如svn,客户端连接到中央服…

C语言——用递归函数计算n!

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言​📝 比别人多一点努力,你…

国产化区块链平台-FISCO BCOS 区块链

目录 FISCO BCOS 版本信息 系统概述 关键特性 组件服务 开发运维工具 FISCO BCOS作为一种企业级区块链平台,为企业和组织提供了高性能、隐私保护和可定制的区块链解决方案。其强大的架构和丰富的功能使得企业能够在安全可信的环境中开展区块链应用&#xff0…

基数排序详解(LSD方法+MSD方法+思路+图解+代码)

文章目录 基数排序一、基数排序概念1.LSD排序法(最低位优先法)2.MSD排序法(最高位优先法) 基数排序 一、基数排序 概念 基数排序是一种非比较型整数排序算法 将整数按位数切割成不同的数字,然后按每个位数分别比较 …