diff演算法(參考snabbdom庫)

2021-10-22 18:04:18 字數 4926 閱讀 8828

snabbdom 是乙個虛擬 dom 庫,專注提供簡單、模組性的體驗,以及強大的功能和效能,據說vue的diff演算法就是參考它的。這裡記錄一下我這幾天學習snabbdom diff演算法的心得和感悟。

我是這樣理解的虛擬dom的,虛擬dom就是將真實dom抽象成乙個物件。當我們要多次操

作dom的時候,我們可以將真實dom轉換為虛擬dom,在虛擬dom中完成相應內容的更改再批量同步到真實dom上去。這樣子可以減小操作dom引起的重排次數。其中,虛擬dom的更新會遵循最小量更新,這就是diff演算法。

diff演算法就是比較兩個虛擬dom的演算法,他是廣度優先的,時間複雜度是o(n)。

在比較兩棵樹時,如果兩顆樹的頂級節點的標籤名或者key值不一樣,diff演算法就會認為這是兩顆不一樣的樹,因此他會銷毀舊的dom樹,再重新建立一棵樹。

對於同一層次的節點,會以key值作為標識來進行,同一節點只移動,不會銷毀後再建立新節點

我們可以看一下vnde的源**

export

default

function

(sel,data,children,text,elm)

;}

可以看到他是將一些引數包裝成乙個虛擬dom這,主要的屬性有:

屬性作用

sel選擇器

data

資料,包含props,class等內容

children

子節點text

文字節點

elm真實dom物件

key標識

import

from

'./vnode'

import

*as is from

'./is'

export type vnodes = vnode[

]export type vnodechildelement = vnode | string | number | undefined |

null

export type arrayorelement<

t>=t

|texport type vnodechildren = arrayorelement

function

addns

(data: any, children: vnodes | undefined, sel: string | undefined)

:void}}

}export

function

h(sel: string)

: vnode

export

function

h(sel: string, data: vnodedata |

null

): vnode

export

function

h(sel: string, children: vnodechildren)

: vnode

export

function

h(sel: string, data: vnodedata |

null

, children: vnodechildren)

: vnode

export

function

h(sel: any, b?

: any, c?

: any)

: vnode

var children: any

var text: any

var i: number

if(c !== undefined)

if(is.

array

(c))

else

if(is.

primitive

(c))

else

if(c && c.sel)

}else

if(b !== undefined && b !==

null

)else

if(is.

primitive

(b))

else

if(b && b.sel)

else}if

(children !== undefined)}if

( sel[0]

==='s'

&& sel[1]

==='v'

&& sel[2]

==='g'

&&(sel.length ===

3|| sel[3]

==='.'

|| sel[3]

==='#'))

return

vnode

(sel, data, children, text, undefined)

};

雖然h函式比較多,但是我們看到它無非做了一件事情·,將傳進來的引數構建成乙個虛擬dom

return

function

patch

(oldvnode: vnode | element, vnode: vnode)

: vnode

// 判斷是不是同乙個虛擬節點,這裡只是判斷了是不是sel和key相同if(

samevnode

(oldvnode, vnode)

)else

}for

(i =

0; i < insertedvnodequeue.length;

++i)

for(i =

0; i < cbs.post.length;

++i) cbs.post[i]()

return vnode

}}

unction createelm

(vnode: vnode, insertedvnodequeue: vnodequeue)

: node

}const children = vnode.children

const sel = vnode.sel

if(sel ===

'!')

vnode.elm = api.

createcomment

(vnode.text!)}

else

if(sel !== undefined)}}

else

if(is.

primitive

(vnode.text)

)const hook = vnode.data!

.hook

if(isdef

(hook))}

}else

return vnode.elm

}

可以看到最後將建立的dom新增到了虛擬dom的lelm屬性上

function

patchvnode

(oldvnode: vnode, vnode: vnode, insertedvnodequeue: vnodequeue)if(

isundef

(vnode.text)

)elseif(

isdef

(ch)

)elseif(

isdef

(oldch)

)elseif(

isdef

(oldvnode.text))}

else

if(oldvnode.text !== vnode.text)

// 新節點文字替換老節點文字

api.

settextcontent

(elm, vnode.text!)}

hook?

.postpatch?

.(oldvnode, vnode)

}

function

updatechildren

(parentelm: node,

oldch: vnode,

// 舊子節點

newch: vnode,

// 新子節點

insertedvnodequeue: vnodequeue)

else

if(oldendvnode ==

null

)else

if(newstartvnode ==

null

)else

if(newendvnode ==

null

)elseif(

samevnode

(oldstartvnode, newstartvnode)

)elseif(

samevnode

(oldendvnode, newendvnode)

)elseif(

samevnode

(oldstartvnode, newendvnode)

)elseif(

samevnode

(oldendvnode, newstartvnode)

)else

idxinold = oldkeytoidx[newstartvnode.key as string]

// 新前節點在舊節點中對應的下標if(

isundef

(idxinold)

)else

else

}// 新前指標移動

newstartvnode = newch[

++newstartidx]}}

if(oldstartidx <= oldendidx || newstartidx <= newendidx)

else

}}

可以看到,更新子節點是比較麻煩的,利用了四個指標,依次按照舊前結點和新前結點,舊后結點與新後結點,舊前結點與新後結點,舊后結點與新前結點來進行比較。這就是sanbbdom的核心diff演算法的大概流程了

diff演算法 diff演算法介紹

diff演算法的作用 計算出virtual dom中真正變化的部分,並只針對該部分進行原生dom操作,而非重新渲染整個頁面。傳統diff演算法 通過迴圈遞迴對節點進行依次對比,演算法複雜度達到 o n 3 n是樹的節點數,這個有多可怕呢?如果要展示1000個節點,得執行上億次比較。即便是cpu快能執...

Diff演算法研究

在unix linux的世界裡面,如果我們需要比較兩個檔案,就會用乙個比較的命令 diff。而這個diff的原理是什麼呢?在diff裡面,我們比較的兩個檔案叫做old和new,而一般是按行來比較。這裡我們可以抽象成乙個字串的比較,比如 old abcdefger new abdefereger 那麼...

diff程式的演算法

diff程式很重要,linux中的源 補丁都是diff作出來的,diff在比較兩個文字檔案的不同方面很高效,它是基於行的,diff會將兩個檔案都按照行分成若干部分,然後計算這些行每一行的校驗碼,之後的問題就是比較這兩個檔案的所有行的校驗碼序列的不同了,這就把問題歸結為了序列比對,diff用的是動態規...