Vue源碼解讀 - diff算法

最近碰到部分業務場景,代碼邏輯須要瞭解"數組變動後,具體變動了哪一些元素,以及變動的位置..."。因而仔細研究並覆寫了一遍針對數組變化的diff算法,在這裏作下diff算法的邏輯分享&&源碼解讀vue

一.介紹前的準備工做

咱們先了解diff方法的運行規則和前提方法.node

1.虛擬node進行深度優先 && 同級對比算法

深度優先: 同級對比 如上面圖所示:數組

每次vnode都是執行同級對比(對應dom同一個父元素)微信

代碼邏輯以下圖: 2.簡單判斷markdown

sameVnode函數用來進行判斷是不是同一個vnode元素。dom

源代碼: 如圖所示: 這裏有兩個重要元素:函數

key : 開發者定義的」:key」優化

sel: 元素tagName+元素id+元素classspa

sel的定義源碼以下: vNode構建函數: 3.構建索引 邏輯如圖:

二.如何處理元素

儘可能不新增/刪除dom,如圖下所示: 若是是相同vnode,源碼以下:

三.開始比較

1.首先會進行時間複雜度 O(n)的while循環,循環條件爲 "遍歷舊節點數組&&遍歷新節點數組,誰先遍歷完循環就結束" ,源碼以下圖: 在每次的循環過程當中,會有兩大類判斷方法:

1-1.首尾比較 && 首尾序號 邏輯:如圖上所示,首先在循環遍歷前 標記好新,舊節點數組的開始位置和結束位置的序號:oldStartIdx、oldEndIdx、newStartIdx、newEndIdx;其次在循環遍歷的過程當中採用 "首首比較,尾尾比較,首尾比較";

源碼以下: 若是數據爲圖上所示,那麼根據首尾比較方法會有以下圖所示結果,最終所有執行了更新操做: 1-2.索引比較 -- 最壞狀況,這裏的時間複雜度也是O(n),即整個算法複雜度O(n)+O(n)

每次遍歷的過程當中可能存在"新數組節點新增/舊數組節點刪除",那麼先後對比就知足不了條件。這裏邏輯會進入索引比較;

好比這種狀況: 那麼,循環中會執行一遍 建立舊數組的索引對象。

那麼從建立到比較的整個邏輯圖以下: 這裏的源碼以下: 1-2.1 當舊節點不存在新增的節點時,進行當前oldStartIdx位置的添加: 源碼以下: 1-2.2 當舊數組存在節點,那麼進行位置移動: 源碼: 1.3 當節點遍歷完以後:

會存在兩種狀況,「新數組已經遍歷完,但舊數組沒有遍歷完成」 || 「舊數組遍歷完成,但新數組沒有遍歷完成」.

故源代碼的判斷以下: 1.3.1 舊數組沒有循環完成:

舊數組沒有循環完成的效果以下圖所示: 這裏注意一個點,咱們每次的節點更新會移動序號,即便被刪除的節點不在一塊 最終也會被 首尾比較算法 "摞在一塊" 即 (oldStartIdx~oldEndIdx)。上圖所示更加明顯一些。

源碼在這裏就進行批量刪除: 1.3.2 新數組沒有循環完成:

效果以下圖所示: 通過 先後對比&&索引 的過濾後,只會存在 新.末尾節點!==舊節點 及以前的連續的新節點(!==舊節點);

因此這裏也被 "摞在一塊" ,即 (newStartIdx~newEndIdx)

源碼以下: 這樣,整個diff的對比算法就已經走完了。因此核心就是:先後對比+索引

四.關鍵點

關鍵點大概以下

五.vue3.0對於diff比較前的優化

vue3.0針對"無腦"patchVnode進行了過濾 -- 靜態類型Vnode:

老版的源碼: 這裏,咱們再重複下vue2.x系列的對比更新邏輯: 新版的vue3.0增長了 靜態類型Vnode,若是是靜態類型的vnode 那麼直接跳過更新,修改新節點引用便可: 備註:comment類型 目前翻到它的源碼也只是更改引用,源碼做者加上了一行註釋: 這裏再多插一句,fragment 碎片類型 爲新增的vnode類型, 即: vue3.0的過濾判斷源碼以下:

更多精彩內容,盡請關注騰訊VTeam技術團隊微信公衆號和視頻號

原做者:陳碧鬆

未經贊成,禁止轉載!

相關文章
相關標籤/搜索