由淺入深讀透vue源碼:diff算法

圖片

導語 | 開發者工作中,研究代碼邏輯常需要思考這個問題:數組變更後,具體變更了哪一些元素?變更的位置如何?本文作者陳碧鬆解析並覆寫了針對數組變化的diff算法邏輯。希望本文對你有幫助。

圖片

diff方法的運行規則和前提方法

爲了瞭解diff方法的運行規則和前提方法,首先我們通過幾個圖快速區別虛擬node進行深度優先和同級對比。

深度優先:

圖片

同級對比:

圖片

如上面圖所示,每次vnode都是執行同級對比。(對應dom同一個父元素)

代碼邏輯如下圖:

圖片

第二,簡單判斷sameVnode函數用來進行判斷是否是同一個vnode元素。源代碼如下:

圖片

如圖所示:

圖片

這裏有兩個重要元素:key : 開發者定義的“:key”;sel :  元素tagName+元素id+元素class。

sel的定義源碼如下:

圖片

vNode構建函數:

圖片

第三是構建索引。

圖片

邏輯如圖:

圖片

圖片

如何處理元素

儘量不新增/刪除dom。如圖下所示:

圖片

如果是相同vnode,源碼如下:

圖片

圖片

開始比較

首先會進行時間複雜度O(n)的while循環,循環條件爲“遍歷舊節點數組&&遍歷新節點數組,誰先遍歷完循環就結束”。源碼如下圖:

圖片

在每次的循環過程中,會有兩大類判斷方法:

1)首尾比較&首尾序號

圖片

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

源碼如下:

圖片

如果數據爲圖上所示,那麼根據首尾比較方法會有如下圖所示結果,最終全部執行了更新操作:

圖片

2)索引比較

最壞情況,這裏的時間複雜度也是O(n),即整個算法複雜度O(n)+O(n)。每次遍歷的過程中可能存在"新數組節點新增/舊數組節點刪除",那麼前後對比就滿足不了條件。這裏邏輯會進入索引比較:比如這種情況:

圖片

那麼循環中會執行一遍,創建舊數組的索引對象。從創建到比較的整個邏輯圖如下:

圖片

這裏的源碼如下:

圖片

  • 當舊節點不存在新增的節點時,進行當前oldStartIdx位置的添加

    圖片

源碼如下:

圖片

  • 當舊數組存在節點,那麼進行位置移動

    圖片

源碼:

圖片

3)當節點遍歷完之後

會存在兩種情況:新數組已經遍歷完,但舊數組沒有遍歷完成;舊數組遍歷完成,但新數組沒有遍歷完成。故源代碼的判斷如下:

圖片

  • 舊數組沒有循環完成

舊數組沒有循環完成的效果如下圖所示:

圖片

這裏注意一個點,我們每次的節點更新會移動序號,即使被刪除的節點不在一塊最終也會被首尾比較算法“摞在一塊”(oldStartIdx~oldEndIdx)。上圖所示更加明顯。源碼在這裏就進行批量刪除:

圖片

  • 新數組沒有循環完成

效果如下圖所示:

圖片

整體來說,有幾個關鍵點:簡單對比;創建舊數組的索引表;首位對比&首尾索引&vnode位置移動;索引添加/位移;剩餘部分批量處理添加/刪除

經過前後對比&索引的過濾後,只會存在新.末尾節點!==舊節點及之前的連續的新節點(!==舊節點),所以這裏也被“摞在一塊”,即 (newStartIdx~newEndIdx)。源碼如下。這樣,整個diff的對比算法就已經走完了。核心就是:前後對比+索引。

圖片

圖片

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

vue3.0針對“無腦”patchVnode進行了過濾--靜態類型Vnode老版的源碼:

圖片

這裏,我們再重複下vue2.x系列的對比更新邏輯:

圖片

新版的vue3.0增加了靜態類型Vnode。如果是靜態類型的vnode,直接跳過更新,修改新節點引用即可。

圖片

comment類型目前翻到它的源碼也只是更改引用,源碼作者加上了一行註釋。

圖片

補充一下,flagment碎片類型爲新增的vnode類型,即:

圖片

vue3.0的過濾判斷源碼如下:

圖片

圖片

數組比較的應用

由於我們想監聽數組的變化,參考了diff算法覆寫類似的邏輯,用來在update,add,dels時,代碼層面獲取操作的具體節點明細(新舊節點的位置,內容)。希望本文對你有幫助。

你可能感興趣的騰訊工程師作品

| 優雅應對故障:QQ音樂怎麼做高可用架構體系?

| 最全Go select底層原理,一文學透高頻用法

| 十億人都在用的健康碼,運維體系是怎麼設計的

|詳解全網最快Go泛型跳錶【內附源碼】

技術盲盒:前端後端AI與算法運維工程師文化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章