Virtual-DOM的理解

jinjiaxing發表於2019-04-20

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