Virtual-DOM的理解
什麼是DOM?
在說Virtual-DOM之前,我們來先說下什麼是DOM,DOM從字面上來理解是文件物件模型。
W3C對DOM的定義是:“一個與系統平臺和程式語言無關的介面,程式和指令碼可以通過這個介面動態地訪問和修改文件內容、結構和樣式。”
而從上面的定義總結來看,DOM是介面,這個介面可以操作文件。
而文件呢就是Document,在HTML中的Document,可以簡單理解成一個節點樹,而我們要將這個節點樹對映成物件,而物件中自然就存在屬性和方法了,其中這些方法就讓我們可以操作文件(好像說的還是有點繞)
什麼是Virtual-DOM?
Virtual-DOM翻譯過來就是虛擬DOM,而它其實可以簡單理解為,通過JS去建立的表示DOM的物件,並且未載入到真實頁面中
- virtual-dom = js物件
- 未渲染到頁面中
有人說用virtual-dom比真實dom快,其實這是相對的,virtual-dom很多時候都不是最優的操作,但它具有普適性,在效率、可維護性之間達平衡
在網上看到一段程式碼,比較簡潔的描述瞭如何去建立一個virtual-dom:
//建立一個VNode的物件
class VNode {
constructor(tag, children, text) {
this.tag = tag
this.text = text
this.children = children
}
render() {
if (this.tag === '#text') {
return document.createTextNode(this.text)
}
let el = document.createElement(this.tag)
this.children.forEach(vChild => {
el.appendChild(vChild.render())
})
return el
}
}
function v(tag, children, text) {
if (typeof children === 'string') {
text = children
children = []
}
return new VNode(tag, children, text)
}
/*
let nodesData = {
tag: 'div',
children: [
{
tag: 'p',
children: [
{
tag: 'span',
children: [
{
tag: '#text',
text: 'baidu'
}
]
}
]
},
{
tag: 'span',
children: [
{
tag: '#text',
text: 'alibaba'
}
]
}
]
}
*/
let vNodes = v('div', [
v('p', [
v('span', [v('#text', 'baidu')])
]
),
v('span', [
v('#text', 'alibaba')
])
]);
console.log(vNodes.render())//建立真實DOM
// 對比DOM樹變更
function patchElement(parent, newVNode, oldVNode, index = 0) {
if (!oldVNode) {
parent.appendChild(newVNode.render())
} else if (!newVNode) {
parent.removeChild(parent.childNodes[index])
} else if (newVNode.tag !== oldVNode.tag || newVNode.text !== oldVNode.text) {
parent.replaceChild(newVNode.render(), parent.childNodes[index])
} else {
for (let i = 0; i < newVNode.children.length || i < oldVNode.children.length; i++) {
patchElement(parent.childNodes[index], newVNode.children[i], oldVNode.children[i], i)
}
}
}
let vNodes1 = v('div', [
v('p', [
v('span', [v('#text', 'baidu')])
]
),
v('span', [
v('#text', 'ali')
])
]
)//虛擬DOM1
let vNodes2 = v('div', [
v('p', [
v('span', [
v('#text', 'jd')
])
]
),
v('span', [
v('#text', 'baidu'),
v('#text', 'map')
])
]
)//虛擬DOM2
const root = document.querySelector('#root')
patchElement(root, vNodes1) //對比更新
複製程式碼
我們從上面的程式碼可以清晰的看到 vdom的簡單流程
vue2引入的vdom是基於snabbdom進行的修改而來,對於snabbdom的原始碼解析,我們可以看這裡
react的diff演算法,在16版本之前,與vue2應該大同小異 而在react16之後的fiber,採用了不同的方式
上面只是簡單的介紹了下virtual-dom的概念而已,而對於具體到底是如何進行vdom之間的diff,才是更核心的東西,我們後續再去研究它
更多內容請看 blog