vue3从精通到入门4:diff算法的实现

Vue 3 的 diff 算法相较于 Vue 2 有了一些改进和优化,主要是为了应对更复杂的组件结构和更高的性能需求。

以下是 Vue 3 diff 算法在处理列表更新时的大致步骤:

  1. 头头比较:首先,比较新旧列表的头节点(即第一个节点)。如果它们相同(基于 key 判断),则复用该节点,并移动两个列表的头指针到下一个节点。

  2. 尾尾比较:然后,比较新旧列表的尾节点(即最后一个节点)。如果它们相同,也复用该节点,并移动两个列表的尾指针到前一个节点。

  3. 移动或创建节点:如果头头比较和尾尾比较都没有找到可复用的节点,Vue 会尝试在旧列表中查找与新节点匹配的节点。如果找到了,则移动该节点到正确的位置;如果没有找到,则创建一个新节点。

  4. 删除节点:最后,检查旧列表中是否有剩余的节点没有被复用或移动。如果有,说明这些节点在新列表中不再需要,因此将它们从 DOM 中删除。

前置节点后置节点比对:

前置节点(头头比较):

比较新旧列表的头节点(即第一个节点)。如果它们相同(基于 key 判断),则复用该节点,并移动两个列表的头指针到下一个节点。

   // 1. sync from start// (a b) c// (a b) d e// 处理相同的前置节点while (i <= e1 && i <= e2) {// 获取索引为 i 的 新老节点 n1 和 n2const n1 = c1[i]const n2 = (c2[i] = optimized? cloneIfMounted(c2[i] as VNode): normalizeVNode(c2[i]))// 判断n1和n2新老节点相同的话,进行节点的更新操作if (isSameVNodeType(n1, n2)) {patch(n1,n2,container,null,parentComponent,parentSuspense,namespace,slotScopeIds,optimized,)} else {// n1 和 n2 不是相同节点话,前置节点的处理结束break}// 循环比对下一对前置节点i++}

后置节点(尾尾比较):

比较新旧列表的尾节点(即最后一个节点)。如果它们相同,也复用该节点,并移动两个列表的尾指针到前一个节点。

// 2. sync from end// a (b c)// d e (b c)// 处理相同的后置节点while (i <= e1 && i <= e2) {// 从最后的节点开始查找,获取的相关节点n1 和 n2const n1 = c1[e1]const n2 = (c2[e2] = optimized? cloneIfMounted(c2[e2] as VNode): normalizeVNode(c2[e2]))// 如果 n1 和 n2 是相同类型节点的话,则进行节点的更新操作if (isSameVNodeType(n1, n2)) {patch(n1,n2,container,null,parentComponent,parentSuspense,namespace,slotScopeIds,optimized,)} else {// 当n1 和 n2 两个新老节点不相同时,处理结束break}e1--e2--}

当比对完前置节点和后置节点后,记录e1、e2、i这三个值,后面需要用到;

仅有新增节点:

在第一步和第二步处理完前后置节点,如果新节点中是仅有新增节点;

源码解析:

根据上图,当 i > e1 并且 i <= e2 就是仅有新增节点

 // 仅有新增节点: 当新节点和旧节点对比时,发现新节点仅有新增节点,只需要将新的节点遍历挂载到新的节点树上if (i > e1) {if (i <= e2) {const nextPos = e2 + 1const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchorwhile (i <= e2) {patch(null,(c2[i] = optimized? cloneIfMounted(c2[i] as VNode): normalizeVNode(c2[i])),container,anchor,parentComponent,parentSuspense,namespace,slotScopeIds,optimized,)i++}}}

仅有卸载节点:

在第一步和第二步处理完前后置节点,如果新节点中是仅有卸载节点;

源码解析:

根据上图,当 i > e2 并且 i <= e1 就是仅有卸载节点

     // 仅有卸载节点:else if (i > e2) {while (i <= e1) {unmount(c1[i], parentComponent, parentSuspense, true)i++}}

乱序的节点:

源码解析:

else {const s1 = i // prev starting index 旧节点索引const s2 = i // next starting index 新节点索引// 5.1 build key:index map for newChildren// 新节点位置映射表, 在前后置节点比较完的中间其余节点都拿出来,放在这个表中const keyToNewIndexMap: Map<string | number | symbol, number> = new Map()for (i = s2; i <= e2; i++) {const nextChild = (c2[i] = optimized? cloneIfMounted(c2[i] as VNode): normalizeVNode(c2[i]))if (nextChild.key != null) {if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) {warn(`Duplicate keys found during update:`,JSON.stringify(nextChild.key),`Make sure keys are unique.`,)}keyToNewIndexMap.set(nextChild.key, i)}}// 5.2 loop through old children left to be patched and try to patch// matching nodes & remove nodes that are no longer presentlet jlet patched = 0// 新节点与旧节点对比后,需要变更的数量const toBePatched = e2 - s2 + 1// 移动标识let moved = false// used to track whether any node has moved// 当前最远位置let maxNewIndexSoFar = 0// works as Map<newIndex, oldIndex>// Note that oldIndex is offset by +1// and oldIndex = 0 is a special value indicating the new node has// no corresponding old node.// used for determining longest stable subsequence// 新旧节点位置映射表,默认值:新节点需要处理的个数const newIndexToOldIndexMap = new Array(toBePatched)for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0for (i = s1; i <= e1; i++) {const prevChild = c1[i]if (patched >= toBePatched) {// all new children have been patched so this can only be a removalunmount(prevChild, parentComponent, parentSuspense, true)continue}let newIndexif (prevChild.key != null) {// 从新节点位置映射表中找旧节点映射值newIndex = keyToNewIndexMap.get(prevChild.key)} else {// key-less node, try to locate a key-less node of the same typefor (j = s2; j <= e2; j++) {if (newIndexToOldIndexMap[j - s2] === 0 &&isSameVNodeType(prevChild, c2[j] as VNode)) {newIndex = jbreak}}}if (newIndex === undefined) {// 当旧节点在新节点位置映射表中没有找到,直接卸载unmount(prevChild, parentComponent, parentSuspense, true)} else {// 当旧节点在新节点位置映射表中找到,更改新旧节点映射表中的值newIndexToOldIndexMap[newIndex - s2] = i + 1// if (newIndex >= maxNewIndexSoFar) {maxNewIndexSoFar = newIndex} else {moved = true}patch(prevChild,c2[newIndex] as VNode,container,null,parentComponent,parentSuspense,namespace,slotScopeIds,optimized,)patched++}}// 5.3 move and mount// generate longest stable subsequence only when nodes have movedconst increasingNewIndexSequence = moved? getSequence(newIndexToOldIndexMap): EMPTY_ARRj = increasingNewIndexSequence.length - 1// looping backwards so that we can use last patched node as anchorfor (i = toBePatched - 1; i >= 0; i--) {const nextIndex = s2 + iconst nextChild = c2[nextIndex] as VNodeconst anchor =nextIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchorif (newIndexToOldIndexMap[i] === 0) {// mount newpatch(null,nextChild,container,anchor,parentComponent,parentSuspense,namespace,slotScopeIds,optimized,)} else if (moved) {// move if:// There is no stable subsequence (e.g. a reverse)// OR current node is not among the stable sequenceif (j < 0 || i !== increasingNewIndexSequence[j]) {move(nextChild, container, anchor, MoveType.REORDER)} else {j--}}}}

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

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

相关文章

《Stable Diffusion AI绘画宝典:从入门到精通,解锁创意新境界》

前沿 在数字化浪潮席卷全球的时代&#xff0c;人工智能以其惊人的创造力和创新性引领着科技新风尚。党的二十大报告明确提出了“实施科教兴国战略&#xff0c;强化现代化建设人才支撑”的宏伟蓝图&#xff0c;展现了我国在新动能、新优势方面的坚定决心和强大气魄。在这个大背…

【Linux】进程的优先级环境变量

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. 进程的优先级2.1 什么是优先级2.2 为什么要有优先级2.3 优先级的查看方式2.4 对优先级调整 3. 命令行参数4. 环境变量4.1 环境变量与配置文件4.1.1 环境变量初步介绍4.1.2 配置文件 4.2 更多环境变量4.3 整…

Postgresql源码(125)游标恢复执行的原理分析

问题 为什么每次fetch游标能从上一次的位置继续&#xff1f;后面用一个简单用例分析原理。 【速查】 恢复扫描需要知道当前页面、上一次扫描到的偏移位置、当前页面一共有几条&#xff1a; 当前页面&#xff1a;HeapScanDesc结构中记录了扫到的页面&#xff08;scan->rs_cb…

Apache Paimon 流式湖仓介绍说明

文章目录 前言选择 Paimon 的原因Apache Paimon 功能一致性保证Paimon 表类型数据湖写入标签和时间线回溯捕获变更数据写入数据湖LSM 和分层文件重用流处理案例使用 Paimon 作为消息队列 前言 Apache Flink 自诞生以来经历了重大演变&#xff0c;如今&#xff0c;它不仅充当批…

毕设选51还是stm32?51太简单?

如果你更倾向于挑战和深入学习&#xff0c;STM32可能是更好的选择。如果你希望更专注于底层硬件原理&#xff0c;51可能更适合。我这里有一套嵌入式入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习嵌入式&#xff0c;不妨点个关注&#xff…

阿里云迁移到AWS云,九河云保姆级教程

随着云计算技术的不断发展,越来越多的企业开始将传统的IT基础设施迁移到云平台上,以获得更高的灵活性、可扩展性和成本效益。在众多云服务提供商中,阿里云和AWS都是备受青睐的选择。本文将探讨如何将阿里云上的资源顺利迁移到AWS云平台,并针对性地进行优化。我们九河云&#xf…

[图解]DDD领域驱动设计伪创新-聚合根06

0 00:00:00,740 --> 00:00:02,200 那刚才讲了 1 00:00:02,480 --> 00:00:04,211 Evans这个隐喻 2 00:00:04,211 --> 00:00:06,520 实际上背后是把集合 3 00:00:06,800 --> 00:00:08,560 当成了聚合 4 00:00:10,580 --> 00:00:14,350 那为什么有这样的一个隐…

OpenHarmony实战开发-如何使用AKI轻松实现跨语言调用。

介绍 针对JS与C/C跨语言访问场景&#xff0c;NAPI使用比较繁琐。而AKI提供了极简语法糖使用方式&#xff0c;一行代码完成JS与C/C的无障碍跨语言互调&#xff0c;使用方便。本示例将介绍使用AKI编写C跨线程调用JS函数场景。通过调用C全局函数&#xff0c;创建子线程来调用JS函…

GIS 数据格式转换

1、在线工具 mapshaper 2、数据上传 3、数据格式转换 导入数据可导出为多种格式&#xff1a;Shapefile、Json、GeoJson、CSV、TopJSON、KML、SVG

APP广告变现项目

APP广告变现项目 很多人觉得不可能&#xff0c;这是肯定存在的&#xff0c;不是现在才有的一个项目&#xff0c;这个项目的原理是怎么样呢&#xff0c;就是通过某些特定的app&#xff0c;然后看完广告就有收益&#xff0c;基本单次的观看单价都是在几毛到1块之间。 养机养好的…

阿里云服务器带宽多少钱?公网带宽收费标准全解析

阿里云服务器的公网带宽计费模式分为“按固定带宽”和“按使用流量”&#xff0c;有什么区别&#xff1f;按固定带宽是指直接购买多少M带宽&#xff0c;比如1M、5M、10M、100M等&#xff0c;阿里云直接分配用户所购买的带宽值&#xff0c;根据带宽大小先付费再使用&#xff1b;…

一套3种风格经典的wordpress免费主题模板

wordpress免费企业主题 https://www.wpniu.com/themes/39.html 免费wordpress企业模板 https://www.wpniu.com/themes/43.html 免费wordpress企业主题 https://www.wpniu.com/themes/44.html

波奇学Linux:ip协议

ip报头是c语言的结构体 报头和有效载荷如何分离&#xff1f; 固定长度四位首部长度 4位版本号就是IPV4 8位服务类型&#xff1a;4位TOS位段和位保留字段 4位TOS分别表示&#xff1a;最小延时&#xff0c;最大吞吐量&#xff0c;最高可靠性&#xff0c;最小成本 给路由器提…

【JAVA基础篇教学】第十六篇:Java连接和操作MySQL数据库

博主打算从0-1讲解下java基础教学&#xff0c;今天教学第十六篇&#xff1a;Java连接和操作MySQL数据库。 我将提供一个简单的示例代码&#xff0c;涵盖数据库连接、查询、插入和更新等操作。 一、下载MySQL驱动包 1.下载地址&#xff1a;MySQL :: Download Connector/J 2.解…

Navicat for MySQL 使用基础与 SQL 语言的DDL

一、目的&#xff1a; Navicat for MySQL 是一套专为 MySQL 设计的高性能数据库管理及开发 工具。它可以用于任何版本 3.21 或以上的 MySQL 数据库服务器&#xff0c;并支持大 部份 MySQL 最新版本的功能&#xff0c;包括触发器、存储过程、函数、事件、视图、 管理用户等。…

VMware配置CentOS 7 并实现ssh连接

Vmware 17下载地址 ***永久许可证&#xff1a;***5Y012-8HL8P-0J8U0-032Q6-93KKF CentOS 7 下载地址 一、配置CentOS 如下 创建新的虚拟机&#xff0c;选择典型&#xff0c;点击下一步 选择上述下载镜像存储位置&#xff0c;选择镜像&#xff0c;点击下一步 3.填写相关信息…

微信小程序wx.getLocation 真机调试不出现隐私弹窗

在小程序的开发过程中&#xff0c;首页中包含要获取用户地理位置的功能&#xff0c;所以在这里的onLoad&#xff08;&#xff09;中调用了wx.getLocation()&#xff0c;模拟调试时一切正常&#xff0c;但到了真机环境中就隐私框就不再弹出&#xff0c;并且出现了报错&#xff0…

智能生活新体验:小米香薰加湿器技术解码

在现代家居生活中&#xff0c;科技与舒适性日益交织&#xff0c;智能家居产品成为提升生活品质的重要工具。小米香薰加湿器作为一款集科技与生活美学于一体的产品&#xff0c;其独特的设计和多功能性受到了广泛欢迎。今天&#xff0c;我们就来详细拆解这款融合了科技与香薰元素…

Sony Camera Remote SDK在Windows上的使用

Sony官方提供了相机遥控软件开发包&#xff0c;允许用户自行开发应用软件&#xff0c;实现对相机的远程控制&#xff0c;包括拍摄、监看和文件传输等。截至目前最新的版本是2024.4.12发布的1.12.00版本&#xff0c;下载链接如下&#xff1a;Camera Remote SDK | LICENSE AGREEM…

初学python记录:力扣706. 设计哈希映射

题目&#xff1a; 不使用任何内建的哈希表库设计一个哈希映射&#xff08;HashMap&#xff09;。 实现 MyHashMap 类&#xff1a; MyHashMap() 用空映射初始化对象void put(int key, int value) 向 HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中&#x…