vue3-dom-diff算法

vue3diff算法

什么是vue3diff算法

Vue3中的diff算法是一种用于比较虚拟DOM树之间差异的算法,其目的是为了高效地更新真实DOM,减少不必要的重渲染

主要过程

整个过程主要分为以下五步

  1. 前置预处理
  2. 后置预处理
  3. 仅处理新增
  4. 仅处理后置
  5. 处理包含新增、卸载、移动的复杂场景

第一步:前置预处理

首先定义一个变量i,记录当前索引值
定义e1、e2记录前置索引值
从前到后遍历新旧索引列表
发现它们的节点值相同,则直接patch打补丁更新
否则跳出循环进入下一步。
vue3源码:

    let i = 0const l2 = c2.lengthlet e1 = c1.length - 1 // prev ending indexlet e2 = l2 - 1 // next ending index// 1. sync from start// (a b) c// (a b) d ewhile (i <= e1 && i <= e2) {const n1 = c1[i]const n2 = (c2[i] = optimized? cloneIfMounted(c2[i] as VNode): normalizeVNode(c2[i]))if (isSameVNodeType(n1, n2)) {patch(n1,n2,container,null,parentComponent,parentSuspense,namespace,slotScopeIds,optimized,)} else {break}i++}

以下demo为例:

i=0 新旧节点都是n1 patch打补丁更新
i=1 新旧节点都是n2 patch打补丁更新
i=2 新节点n3!== n2 跳出循环
在这里插入图片描述

第二步:后置预处理

从后到前去遍历新旧索引列表
发现它们的节点相同,则直接patch
打更新
否则跳出循环
并记录e1 e2的值

vue3源码:

        // 2. sync from end// a (b c)// d e (b c)while (i <= e1 && i <= e2) {const n1 = c1[e1]const n2 = (c2[e2] = optimized? cloneIfMounted(c2[e2] as VNode): normalizeVNode(c2[e2]))if (isSameVNodeType(n1, n2)) {patch(n1,n2,container,null,parentComponent,parentSuspense,namespace,slotScopeIds,optimized,)} else {break}e1--e2--}

以下demo为例:

e1=6、e2=6对应都是n7节点,patch打补丁更新
e1=5、e2=5对应的新旧节点不同
跳出循环,记录e1、e2的值

在这里插入图片描述

第三步:处理仅有新增节点情况

当i > e1 并且i <= e2 :代表仅有新增节点
则直接patch打补丁更新

vue3源码:

     // 3. common sequence + mount// (a b)// (a b) c// i = 2, e1 = 1, e2 = 2// (a b)// c (a b)// i = 0, e1 = -1, e2 = 0if (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++}}}

以下demo为例:
i = 2时,新旧节点不同,从后往前遍历
e1=2、e2=6对应都是n7节点,patch打补丁更新
e1=1、e2=5对应的新旧节点不同
这时i > e1 并且i <= e2,仅有新增节点,直接更新

在这里插入图片描述

第四步:处理仅有卸载节点情况

当i > e2 并且i <= e1 :代表仅有卸载节点
则直接卸载节点

vue3源码:

     // 4. common sequence + unmount// (a b) c// (a b)// i = 2, e1 = 2, e2 = 1// a (b c)// (b c)// i = 0, e1 = 0, e2 = -1else if (i > e2) {while (i <= e1) {unmount(c1[i], parentComponent, parentSuspense, true)i++}}

以下demo为例:
i = 2时,新旧节点不同,从后往前遍历
e1=2、e2=6对应都是n7节点,patch打补丁更新
e1=5、e2=1对应的新旧节点不同
这时i > e2 并且i <= e1 ,仅有卸载节点,直接卸载

在这里插入图片描述

第五步:处理其他情况(新增、卸载、移动)

定义以下变量

变量定义
s1记录旧节点列表要处理部分的起始位置
s2记录新节点列表要处理部分的起始位置
keyToNewIndexMap新节点位置映射表;用于映射新节点与位置的映射关系
newIndexToOldIndexMap新旧节点位置映射表;用于记录新旧节点位置的映射关系。初始值为0,如果都是0,则判定为新节点,需要挂载
maxNewIndexSoFar当前最远位置;用于记录新节点中当前的最远位置。目的是用于判断遍历过程中是否呈现递增趋势。如果不是则代表产生了移动
moved递减趋势,需要移动则标识为true;后续进行移动处理
j最长递增子序列位置

以下demo为列:
变动点:卸载n3 / 新增n8 / 移动n6

在这里插入图片描述
当s1=2 : n3 ;在新节点位置映射表内没有找到;则为卸载,把该节点移除
当s1=3: n4; 在新节点位置映射表中可以找到;则将s1 + 1 = 4放在新旧节点位置映射表中。同时对比新节点位置索引和最远位置,3 > 0,则将新索引位置记录到最远位置中。最后打补丁更新
在这里插入图片描述
当s1=4: n5; 在新节点位置映射表中可以找到;则将s1 + 1 = 5放在新旧节点位置映射表中。同时对比新节点位置索引和最远位置,4 > 3,则将新索引位置记录到最远位置中。最后打补丁更新
在这里插入图片描述

当s1=5: n6; 在新节点位置映射表中可以找到;则将s1 + 1 = 6放在新旧节点位置映射表中。同时对比新节点位置索引和最远位置,2 > 4,呈现递减趋势,说明位置发生了移动,则移动标识为true。最后patch打补丁更新
在这里插入图片描述
到这一步已经遍历完旧节点。这时候需要根据映射表找到最长递增子序列
目的是为了让节点做最小限度的移动操作
从下图中新旧节点映射表中可以看出:最长递增子序列索引值是4/5,将其记录下来,对应的就是1/2
从后开始往前遍历新旧节点映射表
定义变量 i 记录当前新旧节点映射表位置,
定义变量 j 记录最长递增子序列位置,初始化为1
在这里插入图片描述
当i=3:0 对应n8节点。0代表新增,挂载该节点
当i=2: 5 对应n5节点。j=1 对应最长递增子序列。因此无需移动,直接跳转
当i=1: 4 对应n4节点。j=2对应最长递增子序列中。因此无需移动,直接跳转
当i=0: 6对应n6节点。不处于最长递增子序列当中。因此需要移动,执行移动操作
这样下来,整个过程只需要挂载n8节点、卸载n3节点、移动n6节点,其他全部原地patch更新。性能得到了极大的保证

vue3源码:

     // 5. unknown sequence// [i ... e1 + 1]: a b [c d e] f g// [i ... e2 + 1]: a b [e d c h] f g// i = 2, e1 = 4, e2 = 5else {const s1 = i // prev starting indexconst s2 = i // next starting index// 5.1 build key:index map for newChildrenconst keyToNewIndexMap: Map<PropertyKey, 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 = 0const toBePatched = e2 - s2 + 1let moved = false// used to track whether any node has movedlet 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 subsequenceconst 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 + 1if (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--}}}}

以上就是vue3 DOM DIFF算法的整个过程
vue3源码地址: https://github.com/vuejs/core
render文件:vue3\core\packages\runtime-core\src\renderer.ts

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

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

相关文章

【C++】B2093 查找特定的值

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述输入格式输出格式输入输出示例 &#x1f4af;题目分析与解题思路&#x1f4af;代码实现与对比分析我的实现代码老师的实现代码详细对比与分析1. 数组的定义方式2. …

计算机网络:网络层知识点及习题(一)

网课资源&#xff1a; 湖科大教书匠 1、概述 网络层实现主机到主机的传输&#xff0c;主要有分组转发和路由选择两大功能 路由选择处理机得出路由表&#xff0c;路由表再生成转发表&#xff0c;从而实现分组从不同的端口转发 网络层向上层提供的两种服务&#xff1a;面向连接…

CDP集群安全指南-动态数据加密

[〇]关于本文 集群的动态数据加密主要指的是加密通过网络协议传输的数据&#xff0c;防止数据在传输的过程中被窃取。由于大数据涉及的主机及服务众多。你需要更具集群的实际环境来评估需要为哪些环节实施动态加密。 这里介绍一种通过Cloudera Manager 的Auto-TLS功能来为整个…

【HTML】Day02

【HTML】Day02 1. 列表标签1.1 无序列表1.2 有序列表1.3 定义列表 2. 表格标签2.1 合并单元格 3. 表单标签3.1 input标签基本使用3.2 上传多个文件 4. 下拉菜单、文本域5. label标签6. 按钮button7. div与span、字符实体字符实体 1. 列表标签 作用&#xff1a;布局内容排列整齐…

基于Spring Boot的车辆违章信息管理系统(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

Apache Celeborn 在B站的生产实践

背景介绍 Shuffle 演进 随着B站业务的飞速发展,数据规模呈指数级增长,计算集群也逐步从单机房扩展到多机房部署模式。多个业务线依托大数据平台驱动核心业务,大数据系统的高效性与稳定性成为公司业务发展的重要基石。如图1,目前在大数据基础架构下,我们主要采用 Spark、Fl…

第29天:Web开发-PHP应用弱类型脆弱Hash加密Bool类型Array数组函数转换比较

#知识点 1、安全开发-原生PHP-弱类型脆弱 2、安全开发-原生PHP-函数&数据类型 3、安全开发-原生PHP-代码审计案例 一、PHP弱类型对比 1、 和 两个等号是弱比较&#xff0c;使用进行对比的时候&#xff0c;php解析器就会做隐式类型转换&#xff0c;如果两个值的类型不相等就…

Kafaka安装与启动教程

1.下载 先去官网Apache Kafka可以查看到每个版本的发布时间。选择你要安装的版本。 然后进入linux建立要存放的文件夹&#xff0c;用wget命令下载 2.安装 先解压缩&#xff1a; tar -xvzf kafka_2.12-3.5.1.tgz -C ../ 3.配置文件 修改server.properties&#xff1a; cd .…

回归预测 | MATLAB实ELM-Adaboost多输入单输出回归预测

回归预测 | MATLAB实ELM-Adaboost多输入单输出回归预测 目录 回归预测 | MATLAB实ELM-Adaboost多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 一、极限学习机&#xff08;ELM&#xff09; 极限学习机是一种单层前馈神经网络&#xff0c;具有训练速…

1、pycharm、python下载与安装

1、去官网下载pycharm 官网&#xff1a;https://www.jetbrains.com/pycharm/download/?sectionwindows 2、在等待期间&#xff0c;去下载python 进入官网地址&#xff1a;https://www.python.org/downloads/windows/ 3、安装pycharm 桌面会出现快捷方式 4、安装python…

GESP2023年12月认证C++五级( 第三部分编程题(1)小杨的幸运数)

参考程序&#xff1a; #include <iostream> #include <cmath> using namespace std;int nextPerfectSquare(int a) {int sqrt_a (int)sqrt(a);if (sqrt_a * sqrt_a < a) {sqrt_a; // 如果 sqrt(a) 的平方小于 a&#xff0c;那么就需要加 1&#xff0c;找到下…

25年1月更新。Windows 上搭建 Python 开发环境:Python + PyCharm 安装全攻略(文中有安装包不用官网下载)

引言 随着 Python 在数据科学、Web 开发、自动化脚本等多个领域的广泛应用&#xff0c;越来越多的开发者选择它作为首选编程语言。而 PyCharm 作为一个功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;为 Python 开发者提供了极大的便利。本文将详细介绍如何在 …

IDEA配置maven和git并如何使用maven打包和git推送到gitlab

首先找到设置 在里面输入maven然后找到点击 然后点击右边两个选项 路径选择下载的maven目录下的settings文件和新建的repository文件夹 点击apply应用 然后在搜索框里搜git点击进去 此路径为git的exe执行文件所在目录&#xff0c;选好之后点击test测试下方出现git版本号表…

【Vue学习】Vue 组件实例的生命周期(四个阶段,八个钩子)

一、为什么要理解生命周期&#xff1f; 理解生命周期就像是知道了一部电影的剧情走向&#xff0c;能让你在适当的时机做出反应。Vue 生命周期的钩子让你可以在不同的阶段插入你的逻辑&#xff0c;像是提前准备、后期清理或者在数据更新时做点事情。这种“精确控制”的能力会让你…

Linux之信号量

目录 信号量 信号量相关接口 创建信号量 初始化信号量 等待信号量&#xff0c;P操作 发布信号量&#xff0c;V操作 销毁信号量 基于信号量的环形队列下的生产者和消费者模型 环形队列 代码实现 上期我们学习了线程同步的概念&#xff0c;掌握了基于阻塞队列的生产…

【电子通识】PWM驱动让有刷直流电机恒流工作

电机的典型驱动方法包括电压驱动、电流驱动以及PWM驱动。本文将介绍采用PWM驱动方式的恒流工作。 首先介绍的是什么是PWM驱动的电机恒流工作&#xff0c;其次是PWM驱动电机恒流工作时电路的工作原理。 PWM驱动 当以恒定的电流驱动电机时&#xff0c;电机会怎样工作呢&#xff1…

Kafka 消费者专题

目录 消费者消费者组消费方式消费规则独立消费主题代码示例&#xff08;极简&#xff09;代码示例&#xff08;独立消费分区&#xff09; offset自动提交代码示例&#xff08;自动提交&#xff09;手动提交代码示例&#xff08;同步&#xff09;代码示例&#xff08;异步&#…

【游戏设计原理】47 - 超游戏思维

对于这条原理&#xff0c;我首先想到的是开放世界&#xff0c;或者探索性游戏&#xff0c;这是最能包容各类玩家的游戏类型。这类游戏定义了基本规则&#xff0c;玩家的可操作性很强。就像上图里的沙池一样&#xff0c;里面有滑梯&#xff0c;是规则性比较明确的&#xff0c;而…

奥迪TT MK1(初代奥迪TT、第一代奥迪TT)仪表盘故障/不精准/水温/剩余油量不准,如何修复、测试、复位?

故障现象 水温不准&#xff0c;冷启动就130℃汽油加满&#xff0c;指针依然在中间偏左的位置 如下图&#xff1a; 诊断过程 通过VAG KKL 409 USB OBD接口读取水温和油位数值正常&#xff0c;故判断是仪表指针马达损坏或需要重置指针位置 维修步骤 推荐选择CH340&#xff08;老…

Nginx——服务器端集群搭建与扩展模块(五/五)

目录 1.Nginx实现服务器端集群搭建1.1.Nginx 与 Tomcat 部署1.1.1.环境准备 (Tomcat)1.1.2.环境准备 (Nginx) 1.2.Nginx实现动静分离1.2.1.概述1.2.2.需求分析1.2.3.动静分离实现步骤 1.3.Nginx 实现 Tomcat 集群搭建1.4.Nginx 高可用解决方案1.4.1.概述1.4.2.Keepalived 介绍1…