談談對虛擬DOM的理解


一、前言

  一般談論某個東西的時候,我們都想知道它是怎么來的,那我們現在就來說說:虛擬DOM的出現,是跟隨着前端框架React的誕生而誕生的,是由facebook提出來的,主要為了兼顧開發效率與性能,后來其卓越的開發性能也贏得了越來越多的開發者的認可。繼react之后,Vue2.0也在其核心上面引入了虛擬DOM的概念,那接下來我們就來談論一下虛擬DOM的原理:

二、虛擬DOM

  虛擬DOM:virtual dom(下面我們簡稱為vdom,它是vue和react的核心)

  1、什么是vdom?

    vdom:可以看作是一個使用javascript模擬DOM結構的樹形結構。對於我們開發者而言呢,操作DOM結構是非常昂貴的,它改動后,整個容器中的內容中的內容都要重新渲染一遍,就相當於“推倒重來”,如果項目相對來說比較復雜的話,是非常影響性能的。vdom就可以很好地解決這個問題。

    主流的框架均支持使用 JSX 的寫法, JSX 最終會被 babel 編譯為JavaScript 對象,用於來表示vdom,思考下列的 JSX:

    <div>     <span className="item">item</span>      <input disabled={true} />     </div>

    最終會被babel編譯為如下的 JavaScript對象:

    {     type: 'div',      props: null,       children: [{       type: 'span',        props: {     class: 'item',      },     children: ['item'],        },
          {       type: 'input',       props: {      disabled: true,        },     children: [],        }],       }
    
 可以注意到以下兩點:

       所有的 DOM 節點都是一個類似於這樣的對象:{ type: '...', props: { ... }, children: { ... }, on: { ... } }
 

  2、為什么要使用vdom?

    我們簡單的來講vdom和原生的DOM做一個對比(簡單比較一下vdom和原生DOM的重繪過程):

      原生DOM:render html string + 重新創建所有的DOM元素

      vdom:render virtual dom + diff + 必要的dom更新

       和 DOM 操作比起來,js 計算是非常便宜的。vdom render + diff 顯然比原生DOM渲染 html 字符串要慢,但是,它依然是純 js 層面的計算,比起后面的 DOM 操作來說,依然便宜了太多。依次來看,就可以看出vdom很好的提高了渲染效率。

  3、vdom的使用

  上面提到了,我們可以使用vdom來對比出需要對 DOM 進行的特定更改,並單獨進行這些特定更新。讓我們回到我們的示例中,並使用 DOM API 進行相同的更改。

  首先,我們需要制作vdom 的副本,其中包含我們想要進行的更改。由於我們不需要使用 DOM API,因此我們實際上只需創建一個新對象。

const copy = { tagName: "ul", attributes: { "class": "list" }, children: [ { tagName: "li", attributes: { "class": "list__item" }, textContent: "List item one" }, { tagName: "li", attributes: { "class": "list__item" }, textContent: "List item two" } ] }; 

copy 對象可以是用於在當前 vdom(上文的 list 對象)和更新的 vdom 之間對比出一個差異(Diff),Diff 結果如下:

const diffs = [ { newNode: { /* new version of list item one */ }, oldNode: { /* original version of list item one */ }, index: /* index of element in parent's list of child nodes */ }, { newNode: { /* list item two */ }, index: { /* */ } } ] 

  這個 Diff 提供了有關如何更新真實 DOM 的說明。一旦確定了所有差異,我們就可以批量更新 DOM 。

  例如,我們可以循環遍歷每個差異,並根據 Diff 指定的內容添加新的子元素或更新舊的子元素。

const domElement = document.getElementsByClassName("list")[0]; diffs.forEach((diff) => { const newElement = document.createElement(diff.newNode.tagName); /* Add attributes ... */ if (diff.oldNode) { // If there is an old version, replace it with the new version  domElement.replaceChild(diff.newNode, diff.index); } else { // If no old version exists, create a new node  domElement.appendChild(diff.newNode); } })

與框架結合

諸如 React 和 Vue 之類的框架內部使用 Virtual DOM 來對 DOM 進行更高效的更新。例如,我們的 list 組件可以用以下方式用 React 編寫。

import React from 'react'; import ReactDOM from 'react-dom'; const list = React.createElement("ul", { className: "list" }, React.createElement("li", { className: "list__item" }, "List item") ); ReactDOM.render(list, document.body); 

如果我們想要更新列表,我們可以重寫整個列表模板,然后再次調用 ReactDOM.render() ,傳入新列表。

const newList = React.createElement("ul", { className: "list" }, React.createElement("li", { className: "list__item" }, "List item one"), React.createElement("li", { className: "list__item" }, "List item two"); ); setTimeout(() => ReactDOM.render(newList, document.body), 5000); 

因為 React 使用 Virtual DOM,即使我們重新渲染整個模板,也只更新實際更改的部分。如果我們在發生變化時查看我們的開發人員工具,我們將看到特定元素和更改元素的特定部分。

  4、Virtual DOM的優缺點

    優點:

      1、最終表現在DOM上的修改只是變更的部分,可以保證非常高效的渲染。

      2、提升了性能(JavaScript對象比DOM對象性能高),抽象了DOM的具體實現(對DOM進行了一層抽象)

     缺點:

      首次渲染大量DOM時,由於多了一層虛擬DOM的計算,會比innerHTML插入慢。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM