[H5]DOM和Virtual DOM之間的區(qū)別

DOM

  • DOM代表了Document Object Model
  • DOM是一種抽象化的結(jié)構(gòu)性文本
  • DOM的作用只是需要能夠直接獲取東西
    對(duì)于Web Developers來說,這種結(jié)構(gòu)性的文本就是HTML code,并且 DOM被簡(jiǎn)單的稱為HTML DOM。HTML code中的elements就變成了DOM中的nodes。

所以,既然HTML是一種文本,那么DOM就是HTML在內(nèi)存中的表現(xiàn)形式。

將它與程序的一個(gè)實(shí)例進(jìn)行比較。你可以在同一個(gè)程序的多個(gè)過程,就像你可以有多個(gè)相同的HTML DOM(例如同一頁(yè)面加載多標(biāo)簽)。

HTML DOM提供了能夠過去和修改Node的API,例如:getElementByIdremoveChild。
我們通常使用JavaScript來操作DOM對(duì)象。

下面舉個(gè)栗子:

var item = document.getElementById("hei");
item.parentNode.removeChild(item);

document對(duì)象是HTML DOM中root node的一個(gè)抽象體。

遇到的問題

由于HTML文檔的結(jié)構(gòu)允許,所以HTML DOM使用的是樹結(jié)構(gòu)。這就很厲害了,因?yàn)橥ㄟ^樹結(jié)構(gòu)我們可以很容易地遍歷樹。不幸的是,很容易操作并不意味著很快。
現(xiàn)在的WEB程序中,DOM樹?非常巨大。因?yàn)槲覀冊(cè)絹碓酵葡騽?dòng)態(tài)Web應(yīng)用程序(Single Page Applications - SPAs),我們需要大量的、不斷的修改DOM樹。?這真的非常痛苦。
順便說一下,假如我自己設(shè)法創(chuàng)造一個(gè)5GB +源網(wǎng)頁(yè)。它其實(shí)不是很難。但是考慮一下,我們將要在DOM使成千上萬的div。記住,我們是現(xiàn)代的程序猿,我們寫出的代碼應(yīng)該很容易管理!我們的方法,處理事件-點(diǎn)擊提交,很多,型INS…
比方說,典型的jQuery事件處理程序看起來像這一樣:
1、查找對(duì)事件感興趣的每一個(gè)節(jié)點(diǎn)
2、必要時(shí)更新
其中有兩個(gè)問題:

  • 很難管理。
    想象一下,你必須調(diào)整一個(gè)事件處理程序。如果你失去了背景,你必須潛水真正深入到代碼,甚至知道發(fā)生了什么事。

  • 耗時(shí)和錯(cuò)誤風(fēng)險(xiǎn)。
    它的效率很低。我們真的需要手動(dòng)做所有這些調(diào)查結(jié)果嗎?也許我們可以更聰明,并提前告訴哪些節(jié)點(diǎn)是要更新?

那么Virtual DOM則是為了解決以上這些問題。

Virtual DOM

首先需要講明的一點(diǎn)Virtual DOM技術(shù)并不是React發(fā)明的,但是React確實(shí)是使他發(fā)揚(yáng)光大的。

Virtual DOM是HTML DOM的抽象。它是輕量級(jí)和脫離瀏覽器的具體實(shí)施細(xì)節(jié)。因?yàn)镈OM本身已經(jīng)是一個(gè)抽象的、虛擬的DOM,事實(shí)上,Virtual DOM是一個(gè)抽象的抽象。

實(shí)際上,Virtual DOM包含:

  • Javascript DOM模型樹(VTree),類似文檔節(jié)點(diǎn)樹(DOM)
  • DOM模型樹轉(zhuǎn)節(jié)點(diǎn)樹方法(VTree -> DOM)
  • 兩個(gè)DOM模型樹的差異算法(diff(VTree, VTree) -> PatchObject)
  • 根據(jù)差異操作節(jié)點(diǎn)方法(patch(DOMNode, PatchObject) -> DOMNode)

VTree

VTree模型非常簡(jiǎn)單,基本結(jié)構(gòu)如下:

{ 
  // tag的名字 
  tagName: 'p', 
  // 節(jié)點(diǎn)包含屬性 
  properties: { style: { color: '#fff' } }, 
  // 子節(jié)點(diǎn) 
  children: [], 
  // 該節(jié)點(diǎn)的唯一表示,后面會(huì)講有啥用
  key: 1
}

所以我們很容易寫一個(gè)方法來創(chuàng)建這種樹狀結(jié)構(gòu),例如React是這么創(chuàng)建的:

// 創(chuàng)建一個(gè)divreact.createElement('div', null, [ 
  // 子節(jié)點(diǎn)img 
  react.createElement('img', { src: "avatar.png", class: "profile" }), 
  // 子節(jié)點(diǎn)h3 
  react.createElement('h3', null, [[user.firstName, user.lastName].join(' ')])
]);

VTree -> DOM

這方法也不太難,我們實(shí)現(xiàn)一個(gè)簡(jiǎn)單的:

function create(vds, parent) { 
  // 首先看看是不是數(shù)組,如果不是數(shù)組統(tǒng)一成數(shù)組 
  !Array.isArray(vds) && (vds = [vds]); 
  // 如果沒有父元素則創(chuàng)建個(gè)fragment來當(dāng)父元素 
  parent = parent || document.createDocumentFragment(); var node; 
  // 遍歷所有VNode 
  vds.forEach(function (vd) { 
      // 如果VNode是文字節(jié)點(diǎn) 
      if (isText(vd)) { 
        // 創(chuàng)建文字節(jié)點(diǎn) 
        node = document.createTextNode(vd.text); 
        // 否則是元素
      } else { 
        // 創(chuàng)建元素 
        node = document.createElement(vd.tag); 
      } 
      // 將元素塞入父容器 
      parent.appendChild(node); 
      // 看看有沒有子VNode,有孩子則處理孩子
      VNode vd.children && vd.children.length && create(vd.children, node); 
      // 看看有沒有屬性,有則處理屬性 
      vd.properties && setProps({ style: {} }, vd.properties, node); }); 
      return parent;}

diff(VTree, VTree) -> PatchObject

差異算法是Virtual DOM的核心,實(shí)際上該差異算法是個(gè)取巧算法(當(dāng)然你不能指望用O(n^3)的復(fù)雜度來解決兩個(gè)樹的差異問題吧),不過能解決Web的大部分問題。
那么React是如何取巧的呢?

  • 分層對(duì)比

如圖,React僅僅對(duì)同一層的節(jié)點(diǎn)嘗試匹配,因?yàn)閷?shí)際上,Web中不太可能把一個(gè)Component在不同層中移動(dòng)。

  • 基于key來匹配
    還記得之前在VTree中的屬性有一個(gè)叫key的東東么?這個(gè)是一個(gè)VNode的唯一識(shí)別,用于對(duì)兩個(gè)不同的VTree中的VNode做匹配的。



    這也很好理解,因?yàn)槲覀兘?jīng)常會(huì)在Web遇到擁有唯一識(shí)別的Component(例如課程卡片、用戶卡片等等)的不同排列問題。

  • 基于自定義元素做優(yōu)化
    React提供自定義元素,所以匹配更加簡(jiǎn)單。


patch(DOMNode, PatchObject) -> DOMNode

由于diff操作已經(jīng)找出兩個(gè)VTree不同的地方,只要根據(jù)計(jì)算出來的結(jié)果,我們就可以對(duì)DOM的進(jìn)行差異渲染。

總結(jié):

  • 給定一個(gè)可以代表DOM結(jié)構(gòu)的VTree結(jié)構(gòu)
  • 通過vdom/create-element函數(shù)來創(chuàng)建DOM中的節(jié)點(diǎn)
  • 通過將vdom/patch函數(shù)將vtree/diff函數(shù)根據(jù)兩個(gè)不同的vtree結(jié)構(gòu)產(chǎn)生的patch包,更新的DOM中

引用

前沿技術(shù)揭秘
The difference between Virtual DOM and DOM

擴(kuò)展閱讀

Matt-Esch/virtual-dom

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容