詳解vue的diff演算法

2021-09-13 15:26:46 字數 3904 閱讀 1891

前言先來了解幾個點…

1. 當資料發生變化時,vue是怎麼更新節點的?

要知道渲染真實dom的開銷是很大的,比如有時候我們修改了某個資料,如果直接渲染到真實dom上會引起整個dom樹的重繪和重排,有沒有可能我們只更新我們修改的那一小塊dom而不要更新整個dom呢?diff演算法能夠幫助我們。

我們先根據真實dom生成一顆virtual dom,當virtual dom某個節點的資料改變後會生成乙個新的vnode,然後vnode和oldvnode作對比,發現有不一樣的地方就直接修改在真實的dom上,然後使oldvnode的值為vnode。

diff的過程就是呼叫名為patch的函式,比較新舊節點,一邊比較一邊給真實的dom打補丁。

2. virtual dom和真實dom的區別?

virtual dom是將真實的dom的資料抽取出來,以物件的形式模擬樹形結構。比如dom是這樣的:

對應的virtual dom(偽**):

var vnode = ]};

3. diff的比較方式?在採取diff演算法比較新舊節點的時候,比較只會在同層級進行, 不會跨層級比較。

456

上面的**會分別比較同一層的兩個div以及第二層的p和span,但是不會拿div和span作比較。在別處看到的一張很形象的圖:

diff流程圖

當資料發生改變時,set方法會讓呼叫dep.notify通知所有訂閱者watcher,訂閱者就會呼叫patch給真實的dom打補丁,更新相應的檢視。

來看看patch是怎麼打補丁的(**只保留核心部分)

function patch (oldvnode, vnode)  else 

}// some code

return vnode

}

patch函式接收兩個引數oldvnode和vnode分別代表新的節點和之前的舊節點

function samevnode (a, b)
如果兩個節點都是一樣的,那麼就深入檢查他們的子節點。如果兩個節點不一樣那就說明vnode完全被改變了,就可以直接替換oldvnode。

雖然這兩個節點不一樣但是他們的子節點一樣怎麼辦?別忘了,diff可是逐層比較的,如果第一層不一樣那麼就不會繼續深入比較第二層了。(我在想這算是乙個缺點嗎?相同子節點不能重複利用了…)

patchvnode

當我們確定兩個節點值得比較之後我們會對兩個節點指定patchvnode方法。那麼這個方法做了什麼呢?

patchvnode (oldvnode, vnode) else else if (ch)else if (oldch)}}

這個函式做了以下事情:

updatechildren

**量很大,不方便一行一行的講解,所以下面結合一些示例圖來描述一下。

updatechildren (parentelm, oldch, newch) else if (oldendvnode == null) else if (newstartvnode == null) else if (newendvnode == null) else if (samevnode(oldstartvnode, newstartvnode)) else if (samevnode(oldendvnode, newendvnode)) else if (samevnode(oldstartvnode, newendvnode)) else if (samevnode(oldendvnode, newstartvnode)) else 

idxinold = oldkeytoidx[newstartvnode.key]

if (!idxinold)

else else

newstartvnode = newch[++newstartidx]}}

}if (oldstartidx > oldendidx) else if (newstartidx > newendidx)

}

先說一下這個函式做了什麼

**updatechildren

終於來到了這一部分,上面的總結相信很多人也看得一臉懵逼,下面我們好好說道說道。(這都是我自己畫的,求推薦好用的畫圖工具…)

粉紅色的部分為oldch和vch

我們將它們取出來並分別用s和e指標指向它們的頭child和尾child

現在分別對olds、olde、s、e兩兩做samevnode比較,有四種比較方式,當其中兩個能匹配上那麼真實dom中的相應節點會移到vnode相應的位置,這句話有點繞,打個比方

再配個圖

第一步

olds = a, olde = d;

s = a, e = b;

olds和s匹配,則將dom中的a節點放到第乙個,已經是第乙個了就不管了,此時dom的位置為:a b d

第二步

olds = b, olde = d;

s = c, e = b;

olds和e匹配,就將原本的b節點移動到最後,因為e是最後乙個節點,他們位置要一致,這就是上面說的:當其中兩個能匹配上那麼真實dom中的相應節點會移到vnode相應的位置,此時dom的位置為:a d b

第三步

olds = d, olde = d;

s = c, e = d;

olde和e匹配,位置不變此時dom的位置為:a d b

第四步

olds++;

olde--;

olds > olde;

遍歷結束,說明oldch先遍歷完。就將剩餘的vch節點根據自己的的index插入到真實dom中去,此時dom位置為:a c d b

一次模擬完成。

這個匹配過程的結束有兩個條件:

下面再舉乙個例子,可以像上面那樣自己試著模擬一下

當這些節點samevnode成功後就會緊接著執行patchvnode了,可以看一下上面的**

if (samevnode(oldstartvnode, newstartvnode))
就這樣層層遞迴下去,直到將oldvnode和vnode中的所有子節點比對完。也將dom的所有補丁都打好啦。那麼現在再回過去看updatechildren的**會不會容易很多呢?

vue的diff演算法

1.當資料發生變化時,vue是怎麼更新節點的?渲染真實dom的開銷很大,比如我們修改了某個資料,如果直接渲染到真實dom會引起整個dom樹的重繪和重排,有沒有可能我們只更新我們修改的那一小塊dom而不更新整個dom呢?diff演算法可以幫助我們。我們根據真實dom生成乙個虛擬dom,當虛擬dom某個...

Vue 虛擬節點及diff演算法詳解

vue進入2.0以來在其內部加入了虛擬dom的實現,減少了dom的操作,極大提高了效能,同時其diff演算法的時間複雜度為o n 效能很高。首先我們來看下什麼是虛擬dom virtual dom 虛擬dom就是提通過js生成乙個dom物件,之後通過diff演算法比較之後生成patch,即補丁,之後虛...

vue的diff演算法學習

原始碼位址 diff演算法首先要明確乙個概念就是diff的物件是虛擬dom,更新真實dom則是diff演算法的結果 constructor 這個部分的 主要是為了更好地知道在diff演算法中具體diff的屬性的含義,當然也可以更好地了解vnode例項 核心函式是patch函式 isundef判斷 是...