vue3 源码解析(7)— diff 算法源码的实现

前言

vue3 采用的 diff 算法名为快速 diff 算法,整个 diff 的过程分为以下5个阶段完成。

  1. 处理前置节点
  2. 处理后置节点
  3. 处理仅有新增节点
  4. 处理仅有删除节点
  5. 处理其他情况(新增 / 卸载 / 移动)

这里我们先定义新旧两个节点列表,接下来我们通过这两个列表来模拟下 vue3 的 diff 算法的整个过程。

在这里插入图片描述

处理前置节点

从前到后对比两个节点,节点相同则 patch 打补丁更新

在这里插入图片描述
这里我们先定义一个 i 变量用于记录前置索引值并初始化 i = 0,逐个比较它们的节点是否相同。在遍历中可知当 i = 2 的时候新旧节点的值不一样那么我们就停在这里并记录当前 i = 2。经过这一步我们已经处理完成了前面两个节点。对应的 vue3 源码在这里:

let i = 0
const l2 = c2.length
let e1 = c1.length - 1 // prev ending index
let e2 = l2 - 1 // next ending index// 1. sync from start
while (i <= e1 && i <= e2) {const n1 = c1[i]const n2 = c2[i]if (isSameVNodeType(n1, n2)) {patch(n1, n2,container)} else {break}i++
}

处理后置节点

从后往前对比两个节点,节点相同则 patch 打补丁更新

在这里插入图片描述

这里我们定义一个 e1 e2 分别记录新旧节点列表的后置索引值。在遍历中可知当 e1 = 5 e2 = 5 的时候新旧节点的值不一样那么我们就停在这里并记录当前 e1e2。经过这一步我们已经处理完成了后面一个节点。对应的 vue3 源码在这里:

// 2. sync from end
while (i <= e1 && i <= e2) {const n1 = c1[e1]const n2 = c2[e2]if (isSameVNodeType(n1, n2)) {patch(n1, n2,container)} else {break}e1--e2--
}

处理仅有新增节点

新节点还没遍历完而旧节点已遍历结束

在这里插入图片描述
在遍历过程中会出现 i > e1 && i <= e2 的情况(旧的少 新的多),这种情况代表有节点需要被新增。对应的 vue3 源码在这里:

if (i > e1) {if (i <= e2) {const nextPos = e2 + 1 // 插入的位置const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor // 参照物while (i <= e2) {patch(null, c2[i],container, anchor)i++}}
}

处理仅有删除节点

新节点已遍历结束而旧节点还没遍历完

在这里插入图片描述
在遍历过程中同样也会出现 i > e2 的情况(旧的多 新的少),这种情况代表有节点需要被删除。对应的 vue3 源码在这里:

if (i > e2) {while (i <= e1) {unmount(c1[i], parentComponent, parentSuspense, true)i++}
}

处理其他情况(新增 / 卸载 / 移动)

在完成前置后置预处理后,最后我们来处理有新增、卸载、移动的复杂情况。让我们来看下 vue3 是如何完成这种复杂情况的操作。我们先定义如下几个变量:

  1. s1 s2 分别记录新旧节点列表要处理部分的起始位置。
  2. keyToNewIndexMap 用于保存新节点与位置的索引关系。
  3. moved = false 表示移动标识。
  4. maxNewIndexSoFar = 0 用于记录新节点中当前的最远位置,目的是用于判断新旧节点在遍历过程中是否呈现递增趋势,如果不是则证明节点产生了移动,需要设置 moved = true 后续进行移动处理。
  5. newIndexToOldIndexMap 用于记录新旧节点位置的映射关系,初始值都为 0,如果处理完之后还是保持 0 这个值的话则判定为新节点后续需要挂载。

在这里插入图片描述

const s1 = i // prev starting index
const s2 = i // next starting index// 5.1 build key:index map for newChildren
const keyToNewIndexMap = new Map()
for (i = s2; i <= e2; i++) {const nextChild = c2[i]if (nextChild.key != null) {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 present
let j
let 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] = 0

接下来我们从旧节点列表中依次取值并判断获取到的值是否存在于新节点列表中其结果只会包含两种情况:存在和不存在。

  • 不存在:直接卸载该节点。
  • 存在:
    1. 更新 newIndexToOldIndexMap 对应下标的值。
    2. 对比新节点位置索引值和当前最远位置如果 newIndex >= maxNewIndexSoFar 则更新 maxNewIndexSoFar = newIndex 的值,否则的话更新 moved = true

在这里插入图片描述
对应的 vue3 源码在这里:

for (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 + 1if (newIndex >= maxNewIndexSoFar) {maxNewIndexSoFar = newIndex} else {moved = true}patch(prevChild, c2, container,null)patched++}
}

需要注意的是仅当节点移动时才生成最长递增子序列,其目的是让节点可以做到最小限度的移动操作

在这里插入图片描述

接下来从后开始往前遍历处理新旧节点位置映射表:

  • i = 3 对应的位置值为 0 表示该节点为新节点后续需要挂载。
  • i = 2 处于最长递增子序列 j = 1 的地方,直接跳过,同时 ij 需要同时往上移动。
  • i = 1 处于最长递增子序列 j = 0 的地方,直接跳过,同时 ij 需要同时往上移动。
  • i = 0 对应的位置值为 6,i = 0 不处于最长递增子序列中因此该节点需要移动。

整个过程只是新挂载了 n8 节点、卸载了 n3 节点、移动了 n6 节点,其他均为原地 patch 更新,这样的处理使性能得到了极大的提升。

在这里插入图片描述
对应的 vue3 源码在这里:

// 5.3 move and mount
// generate longest stable subsequence only when nodes have moved
const increasingNewIndexSequence = moved? getSequence(newIndexToOldIndexMap): EMPTY_ARR
j = increasingNewIndexSequence.length - 1// looping backwards so that we can use last patched node as anchor
for (i = toBePatched - 1; i >= 0; i--) {const nextIndex = s2 + iconst nextChild = c2[nextIndex]const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1.el : parentAnchorif (newIndexToOldIndexMap[i] === 0) {// mount newpatch(null, nextChild, container, anchor)} 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--}}
}

由于 vue3 diff 算法源码内容较多这里就不在贴出来,最后附上源码链接感兴趣的小伙伴可以深入的了解下。

总结

以上就是 vue3 diff 算法的大致流程,本篇文章为 【前端面试】Vue3 DOM Diff】的文字记录版。如有不明白或者是写错的地方还希望大家可以指出来,最后码字不易,希望大家可以素质三连。

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

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

相关文章

光距感-接近传感芯片的工作原理以及应用领域

接近光传感芯片是一种可以检测物体距离和位置的传感器。它的工作原理基于光电效应。当某个物体与接近光传感器靠近时&#xff0c;传感器会发出一束红外线。随着物体越来越靠近&#xff0c;被反射回来的光线会变强&#xff0c;被接近光传感器捕获。传感器可以测量时间&#xff0…

PDF文档电子签名怎么做?

如何确保电子文档的签署具有公信力和法律效力&#xff0c;防止伪造和假冒签名等问题&#xff0c;是电子文档无纸化应用面临的重要挑战。本文将详细介绍PDF文档电子签名的概念、重要性、实施步骤以及相关的法律背景&#xff0c;帮助用户理解并有效应用PDF文档电子签名技术。 1.…

树和二叉树(一)

一、树 非线性数据结构&#xff0c;在实际场景中&#xff0c;存在一对多&#xff0c;多对多的情况。 树( tree&#xff09;是n (n>0&#xff09;个节点的有限集。当n0时&#xff0c;称为空树。 在任意一个非空树中&#xff0c;有如下特点。 1.有且仅有一个特定的称为根的节点…

西晋从建立到灭亡51年历史

西晋的建立&#xff0c;也标志着三国时期的结束&#xff0c;也开启了中国历史上的一个新时代。下面让我们来揭开西晋从建立到灭亡51年的历史。 1、高平陵事变 曹丕去世后&#xff0c;魏明帝曹睿继位&#xff0c;但曹睿却英年早逝&#xff0c;幼子曹芳继位。 司马懿受曹睿托孤…

全球首个AI女团Sorai.ai出道:定档4月19日北京电影节出道首秀

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

关于 Windows10 计算机丢失 MSVCP120.dll 的解决方法

今天学长跟平时一样打开电脑开始发布文章需要用到Adobe Photoshop CC 2018的时候居然给我来个Photoshop.exe-系统错误、无法启动此程序&#xff0c;因为计算机中丢失MSVCP120.dll 尝试重新安装该程序以解决此问题&#xff0c;安装上面的说明重新安装了我的Photoshop CC 打开还是…

转换为elementUI提示方法为uni-app的showToast提示

// 转换为elementUI提示方法为uni-app的showToast提示---------------------------------------- // 一般提示 Vue.prototype.$message function(title) {title && uni.showToast({icon: none,title}); }; // 成功提示 Vue.prototype.$message.success (title) > …

泰迪智能科技携手洛阳理工学院共建“泰迪·洛阳理工数据智能工作室”

为深化校企合作&#xff0c;实现应用型人才培养目标&#xff0c;4月11日&#xff0c;洛阳理工学院携手广东泰迪智能科技股份有限公司举行“泰迪洛阳理工数据智能工作室”揭牌仪式暨工作室成员动员会在洛阳理工学院举行。洛阳理工学院计算机与信息工程学院院长石念峰、副院长李明…

【Pytorch】Conv1d

conv1d 先看看官方文档 再来个简单的例子 import torch import numpy as np import torch.nn as nndata np.arange(1, 13).reshape([1, 4, 3]) data torch.tensor(data, dtypetorch.float) print("[data]:\n", data) conv nn.Conv1d(in_channels4, out_channels1…

【每日刷题】Day16

【每日刷题】Day16 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; 2. 160. 相交链表 - 力扣&…

智慧园区解决方案一站式资料大全:标准规范顶层设计指南、供应商整体解决方案及售前PPT、标准白皮书、全国前50智慧园区集成商方案等全套600份,一次性打包下载

关键词&#xff1a;智慧园区解决方案&#xff0c;智慧园区整体解决方案&#xff0c;智慧园区建设总体方案设计&#xff0c;智慧园区综合管理系统&#xff0c;智慧产业园区解决方案&#xff0c;智慧产业园区规划方案&#xff0c;智慧园区建设规划方案&#xff0c;智慧工业园区建…

IO流-IO框架

简介 java的IO流操作提供了最简单的操作&#xff0c;第三方基于日常使用习惯&#xff0c;写了很多IO框架&#xff0c;更加方便操作避免重复造轮子&#xff0c;提高开发效率 Commons-io 简介 Commons-io是apche提供的IO操作的小框架 部分常用的API 引入依赖 <dependency>…