1 diff演算法到底是什麼? diff演算法是一種通過同層的樹節點進行比較的高效演算法,它可以不用頻繁操作DOM,而是選用虛擬DOM節點操作,說人話就是專門用來處理虛擬DOM節點的。 2 操作流程? 為了更好理解Vue的diff演算法,請先看一位B站大佬精心製作的 動畫演示。 通過上面視頻可以很好理解di ...
1 diff
演算法到底是什麼?
diff
演算法是一種通過同層的樹節點進行比較的高效演算法,它可以不用頻繁操作DOM
,而是選用虛擬DOM
節點操作,說人話就是專門用來處理虛擬DOM節點的。
2 操作流程?
為了更好理解Vue
的diff
演算法,請先看一位B站大佬精心製作的
動畫演示。
通過上面視頻可以很好理解diff演算法的比較流程,清楚在迴圈從左右兩邊向中間比較的更新、插入、刪除、查詢操作。
它的操作本質就是:
分別遍歷新舊虛擬DOM
節點的數組,接著通過迴圈左右雙指針比較判斷。
新的頭 newStartIndex 和老的頭 oldStartIndex 對比
新的尾 newEndIndex 和老的尾 oldEndIndex 對比
新的頭 newStartIndex 和老的尾 oldEndIndex 對比
新的尾 newEndIndex 和老的頭 oldStartIndex 對比
3 解析github源碼
我看到掘金的一篇講的很不錯,可以看看,深入淺出虛擬 DOM 和 Diff 演算法,及 Vue2 與 Vue3 中的區別,我就只負責總結一下學習筆記吧。
而且它講的Vue3涉及到diff的內容也很易理解。
3.1 patch函數:對比新舊虛擬DOM
3.1.1 什麼時候觸發?
在頁面首次渲染的時候會調用一次 patch
並創建新的 vnode
,不會進行更深層次的比較。
在組件中數據發生變化時:
- 先觸發
setter
然後通過Notify
通知Watcher
。 - 對應的
Watcher
會通知更新並執行更新函數,它會執行render
函數獲取新的虛擬DOM
。 - 執行
patch
對比上次渲染結果的老的虛擬DOM
,並計算出最小的變化,然後再去根據這個最小的變化去更新真實的DOM
,也就是視圖View
3.1.2 patch的更新流程流程(源碼第700行)
3.2 patchVnode函數:對比節點文本變化或子節點變化
- 如果
oldVnode
和vnode
的引用地址是一樣的,就表示節點沒有變化,直接返回。 - 通過
oldVnode
的isAsyncPlaceholder
判斷註釋、v-if
和非同步函數的情況,選擇跳過非同步組件的檢查,直接返回。 - 如果
oldVnode
和vnode
都是靜態節點,有相同的key
vnode
是克隆節點或者v-once
指令控制的節點時- 把
oldVnode.elm
和oldVnode.child
都複製到vnode
上,然後返回。
- 把
- 如果
vnode
不是文本節點也不是註釋的情況下:- 如果
vnode
文本為undefined
,就刪掉vnode.elm
文本 - 如果
vnode
和oldVnode
都有子節點,而且子節點不一樣的話,就調用updateChildren
更新子節點 - 如果只有
vnode
有子節點,就調用addVnodes
創建子節點 - 如果只有
oldVnode
有子節點,就調用removeVnodes
刪除該子節點
- 如果
- 如果
vnode
是文本節點但是和oldVnode
文本內容不一樣,就更新文本
3.3 updateChildren:對比子節點的函數
當每輪迴圈對比時都不能滿足找到對應的key值與標簽值一致的情況時,那麼要不斷拿 新的開始節點 的 key 去 老的開始節點的子節點 children 找。
- 如果沒找到,就創建一個新的節點
- 如果找到了,再對比標簽是不是同一個節點
- 如果是同一個節點,就調用 patchVnode 進行後續對比,然後把這個節點插入到 老的開始節點 前面,並且移動新的開始下標,繼續下一輪迴圈對比
- 如果不是相同節點,就創建一個新的節點
- 如果老的 vnode 先遍歷完,就添加新的 vnode 沒有遍歷的節點
- 如果新的 vnode 先遍歷完,就刪除老的 vnode 沒有遍歷的節點