虛擬dom的實現和diff演算法

2021-09-27 12:55:14 字數 4353 閱讀 1064

現在主流框架都採用虛擬dom,為什麼?

乙個真實dom的生成代價很昂貴,不過js的執行速度很快,這樣我們通過對js的虛擬dom樹操作,通過diff演算法來對真實dom做出相應的操作,效率事倍功半。

首先看看乙個虛擬dom的結構,其實就是乙個js的物件,裡面包含了各種需要實現的屬性:

const ul =

, children:[,

children:

},]}

virtual dom生成真實dom:

class

element

= vdom

this

.key = props ? props.key :

void

666// 這邊比較重要,dfs的標記,為後面leftnode標記演算法使用

let count =

0 children.

foreach

((child, i)

=>

count ++})

this

.count = count

}render()

=this

.vitrual

el = document.

createelement

(tagname)

const fregment = children ? document.

createdocumentfragment()

:null

children && children.

map(elem =>

) fregment && el.

(fregment)

return el

}}// 遍歷virtual dom, dfs遍歷

function

createtree

(vdom)

= vdom

vdom.children = children && children.

map(v =>

(v instanceof

object)?

createtree

(v): v

)return

newelement

(vdom)

}

diff演算法:先找變化的地方,通過標記,改變真是dom;

先找diff

replace 替換節點

props 修改屬性

reorder children的重排

text 文字內容的替換

首先通過dfs方式遍歷標記找到diff

import _ from

'./utils'

import listdiff from

'./diff-list'

varreplace=0

varreorder=1

varprops=2

vartext=3

function

diff

(oldtree, newtree)

// 存放所有的變更

dfswalk

(oldtree, newtree, index, patches)

return patches

}// dfs計算diff

function

dfswalk

(oldnode, newnode, index, patches)

else

if(_.

isstring

(oldnode)

&& _.

isstring

(newnode)))

}}else

if(oldnode.tagname === newnode.tagname &&

oldnode.key === newnode.key)

)// 這邊不管ignore

diffchildren

( oldnode.children,

newnode.children,

index,

patches,

currentpatch

)}else)}

}// 比較list

function

diffchildren

(oldchildren, newchildren, index, patches, currentpatch))}

let leftnode =

null

let currentnodeindex = index

oldchildren.

foreach

((child, i)

=>)}

// 比較props

function

diffprops

(oldnode, newnode)

const oldprops = oldnode.props

const newprops = newnode.props

for(const

[key, val]

of object.

entries

(oldprops))}

for(

const key of object.

keys

(newprops))}

const

= object.

keys

(propspatches)if(

!length)

return

null

return propspatches

}export

default diff

上面找出對於的diff存入patches中,在應用修改對應的diff

講講list-diff2的對比:

通過把新老的children進行對比,通過key值對比,不存在key的項存入free陣列然後對應覆蓋,其他將被移除的key元素,用null代替,標記為待移除(remove)存入moves陣列,新增的key元素(待插入)加入moves陣列,返回乙個物件,所以在列表遍歷時候框架會提示需要傳入key,最大的原因就是為了virtual dom的diff操作,這邊是乙個example:

這邊第二個元素被移除用null代替,並在moves列表新增改元素的刪除操作;

type: 0 刪除 1 新增插入

然後children為何oldchildren對應長度的列表,繼續走正常的diff操作。

找出了diff後,那就應用diff進行對真實dom進行修改:

import _ from

'./utils'

varreplace=0

varreorder=1

varprops=2

vartext=3

function

patch

(node, patches)

dfswalker

(node, walker, patches)

}function

dfswalker

(node, walker, patches))if

(currentpatches)

(node, currentpatches)

}function

(node, currentpatches)})

}function

setprops

(node, props)

}function

setreplace

(node, currentpatch)

function

setreorder

(node, moves)

const staticnodes = array.

from

(node.childnodes)

staticnodes.

foreach

(child =>}}

) moves.

foreach

((move, i)

=>

= move

if(move.type ===0)

}else})

}export

default patch

後續對應四個不同的操作進行修改即可。

虛擬DOM和Diff演算法

虛擬dom其實就是在真實dom之前加了一層js物件生成的dom 用js物件模擬dom 把這個虛擬dom物件轉為真實的dom插入到頁面中 如果有事件修改了虛擬dom,比較兩個虛擬dom樹的差異,得到差異物件 補丁 把差異物件應用到正則的dom樹上 比較同級的節點 同一父節點的子節點 當發現節點已經不存...

虛擬DOM和diff演算法

虛擬dom virtual dom 也就是我們常說的虛擬節點,它是通過js的object物件模擬dom中的節點,然後再通過特定的render方法將其渲染成真實的dom的節點。為什麼要使用虛擬dom呢,因為操作真實dom的耗費的效能代價太高,頻繁的操作dom,會大量的造成頁面的重繪和回流,出於效能優化...

虛擬DOM和 Diff演算法

1 用js來模擬dom中的節點。傳說中的虛擬dom。虛擬dom實現過程 1.根據真實dom產生虛擬dom?虛擬dom?就是乙個特殊物件 經過一些處理能產生真實dom 2.進行編譯解析 3.將虛擬dom 變成真實dom 也就 掛載 4.資料發生變化 產生新的虛擬dom 5 將資料變化後的虛擬dom 和...