現在主流框架都採用虛擬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的對比:找出了diff後,那就應用diff進行對真實dom進行修改:通過把新老的children進行對比,通過key值對比,不存在key的項存入free陣列然後對應覆蓋,其他將被移除的key元素,用null代替,標記為待移除(remove)存入moves陣列,新增的key元素(待插入)加入moves陣列,返回乙個物件,所以在列表遍歷時候框架會提示需要傳入key,最大的原因就是為了virtual dom的diff操作,這邊是乙個example:
這邊第二個元素被移除用null代替,並在moves列表新增改元素的刪除操作;
type: 0 刪除 1 新增插入
然後children為何oldchildren對應長度的列表,繼續走正常的diff操作。
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 和...