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.pswp.cn/web/65194.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/65194.shtml
英文地址,請注明出處:http://en.pswp.cn/web/65194.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Dell服務器升級ubuntu 22.04失敗解決

ubuntu系統原版本20.04&#xff0c;服務器dell T40. 執行apt update后&#xff0c;再執行apt upgrade。 apt update執行成功&#xff0c;但apt upgrade執行中斷&#xff0c;提示如下&#xff1a; Checking package manager Reading package lists... Done Building dependen…

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

博客主頁&#xff1a; [小????????] 本文專欄: C 文章目錄 &#x1f4af;前言&#x1f4af;題目描述輸入格式輸出格式輸入輸出示例 &#x1f4af;題目分析與解題思路&#x1f4af;代碼實現與對比分析我的實現代碼老師的實現代碼詳細對比與分析1. 數組的定義方式2. …

計算機網絡:網絡層知識點及習題(一)

網課資源&#xff1a; 湖科大教書匠 1、概述 網絡層實現主機到主機的傳輸&#xff0c;主要有分組轉發和路由選擇兩大功能 路由選擇處理機得出路由表&#xff0c;路由表再生成轉發表&#xff0c;從而實現分組從不同的端口轉發 網絡層向上層提供的兩種服務&#xff1a;面向連接…

CDP集群安全指南-動態數據加密

[〇]關于本文 集群的動態數據加密主要指的是加密通過網絡協議傳輸的數據&#xff0c;防止數據在傳輸的過程中被竊取。由于大數據涉及的主機及服務眾多。你需要更具集群的實際環境來評估需要為哪些環節實施動態加密。 這里介紹一種通過Cloudera Manager 的Auto-TLS功能來為整個…

Swift Protocols(協議)、Extensions(擴展)、Error Handling(錯誤處理)、Generics(泛型)

最近在學習 Swift&#xff0c;總結相關知識 1. Protocols&#xff08;協議&#xff09; 1.1 協議的定義和實現 協議&#xff08;protocol&#xff09; 是一種定義方法和屬性的藍圖&#xff0c;任何類、結構體或枚舉都可以遵循協議。遵循協議后&#xff0c;需要實現協議中定義…

uni-app開發-習慣養成小程序/app介紹

目錄 一:功能概述 二:功能部分代碼和截圖 一:功能概述 1 習慣目標生成 創建習慣:用戶可以添加新的習慣目標,每個習慣可以包含名稱、描述、圖標、目標天數。 關聯習慣完成:用戶通過設定達成目標以后,生成習慣養成記錄。 2 習慣打卡 簡單快捷的打卡:提供一個直觀的界面…

【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;…

Git - 記錄一次由于少輸入了一個命令導致的更改丟失

Git - 記錄一次由于少輸入了一個參數導致的更改丟失 前言 某晚我激情開發了幾個小時&#xff0c;中途沒有進行commit存檔。準備睡覺時&#xff0c;我想創建一個新的分支并將今晚所有更改提交到新分支上&#xff08;似乎應該開發時候就創建&#xff1f;&#xff09;。 然后因…

探索Rust在Web開發中的實際應用

近年來&#xff0c;Rust語言因其高性能、內存安全性和強大的工具鏈支持而迅速崛起。在Web開發領域&#xff0c;Rust提供了一套高效、現代化的框架和工具&#xff0c;使得開發者能夠構建快速、安全的Web應用程序。在本文中&#xff0c;我們將深入探討如何使用Rust進行Web開發&am…

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版本號表…

【Rust 知識點雜記】

1、self和Self 在Rust中&#xff0c;self 和 Self 有不同的含義和用法&#xff0c;它們通常出現在結構體、枚舉或實現&#xff08;impl&#xff09;塊的上下文中。 self: self 是一個關鍵字&#xff0c;它代表方法調用時實例本身的引用。當在一個方法定義中使用 self 作為第一…

【Vue學習】Vue 組件實例的生命周期(四個階段,八個鉤子)

一、為什么要理解生命周期&#xff1f; 理解生命周期就像是知道了一部電影的劇情走向&#xff0c;能讓你在適當的時機做出反應。Vue 生命周期的鉤子讓你可以在不同的階段插入你的邏輯&#xff0c;像是提前準備、后期清理或者在數據更新時做點事情。這種“精確控制”的能力會讓你…