「Virtual Dom 的優點是什麼?」 這是一個常見的面試問題,可是答案真的僅僅是簡單粗暴的一句「直接操做dom和頻繁操做dom的性能不好」就完事了嗎?若是是這樣的話,不妨繼續深刻地問幾個問題:javascript
爲何Virtual Dom可以避免直接操做dom引發的問題?html
若是發現本身對這些問題不(yi)太(lian)確(meng)定(bi),那麼不妨往下讀一讀。vue
Virtual Dom,也就是虛擬的Dom, 不管是在React仍是Vue都有用到。它自己並非任何技術棧所獨有的設計,而是一種設計思路,或者說設計模式。java
在介紹虛擬dom以前,首先來看一下與之相對應的真實Dom:node
DOM(Document Object Model)
的含義有兩層:react
the object-based representation
);形如如下的html
代碼,web
<!DOCTYPE html> <html lang="en"> <head> <title>Document</title> </head> <body> <h1>Learning Virtual Dom</h1> <ul class="list"> <li class="list-item">List item</li> </ul> </body> </html>
根據DOM會被表示爲以下一棵樹: 樹的每一個分支的終點都是一個節點(node),每一個節點都包含着對象,包含一些節點屬性。 這就是基於對象來表示文檔。面試
其次,DOM容許咱們經過一些的api對文檔進行操做,例如:設計模式
const listItemOne = document.getElementsByClassName("list-item")[0]; // 獲取節點 listItemOne.textContent = "List item one"; // 修改對應的文本內容 const listItemTwo = document.createElement("li"); // 建立一個元素對象 listItemTwo.classList.add("list-item"); // 添加子元素 listItemTwo.textContent = "List item two"; list.appendChild(listItemTwo);
簡而言之。DOM的做用就是把web頁面和腳本(一般是指Javascript)關聯起來。api
那麼原生的DOM操做存在哪些問題呢?在此還須要瞭解到瀏覽器工做的一些流程,一般來講,一個頁面的生成須要經歷如下步驟:
其中第4步和第5步其實就是常說的頁面渲染,而渲染的過程除了在頁面首次加載時發生,在後續交互過程當中,DOM操做也會引發從新排列和從新繪製,渲染是須要較高性能代價的,尤爲是重排的過程。
因此常見的優化思路都會提到一點: 爲了儘量減小重繪和重排次數,儘可能把改變dom的操做集中在一塊兒,由於寫入操做會觸發重繪或者重排,而且瀏覽器的渲染隊列機制是:當某個操做觸發重排或重繪時,先把該操做放進渲染隊列,等到隊列中的操做到了必定的數量或者到了必定的時間間隔時,瀏覽器就會批量執行。因此集中進行dom操做能夠減小重繪重排次數。
另外一方面,關於DOM操做的影響範圍問題:因爲瀏覽器是基於流式佈局的,因此一旦某個元素重排,它的內部節點會受到影響,而外部節點(兄弟節點和父級節點等等)是有可能不受影響的,這種局部重排引發的影響比較小,因此也須要儘量地每次只改動最須要的節點元素。
Virtual DOM 就是爲了解決上面這個問題而生的,它爲咱們操做dom
提供了一種新的方式。
virtual DOM 的本質就是真實dom的一個副本,無需使用DOM API,就能夠頻繁地操做和更新此副本。 對虛擬DOM進行全部更新後,咱們能夠查看須要對原始DOM進行哪些特定更改,並以針對性和優化的方式進行更改.
這個思路能夠參照行軍打仗時的沙盤,沙盤的一個做用就是模擬軍隊的排列分佈。設想一下不借助沙盤時的場景:
將軍1: 我以爲三隊的士兵應該往東邊移動200米,側翼埋伏,而後傳令官跑去通知三隊的士兵,吭哧吭哧跑了200米;
將軍2: 我以爲四隊的士兵應該往西邊移動200米,和三隊造成合圍之勢,而後傳令官繼續通知,四隊的士兵也繼續奔跑。
將軍3:我以爲埋伏的距離太遠了,近一點比較好, 兩隊各向中間移動100米吧。
而後可憐的士兵們繼續來回跑....
在這個過程裏每次行軍移動都要帶來大量的開銷,每次都直接用實際行動執行還在商討中的指令,成本是很高的。實際上在將軍們探討商量佈陣排列時,能夠
這也就是 Virtual DOM 要作的事。
那麼 Virtual DOM大概是什麼樣呢? 仍是按照前面的html文件,對應的virtual dom
大概長這樣(不表明實際技術棧的實現,只是體現核心思路):
const vdom = { tagName: "html",// 根節點 children: [ { tagName: "head" }, { tagName: "body", children: [ { tagName: "ul", attributes: { "class": "list" }, children: [ { tagName: "li", attributes: { "class": "list-item" }, textContent: "List item" } // end li ] } // end ul ] } // end body ] } // end html
咱們用一棵js
的嵌套對象樹表示出了dom樹的層級關係以及一些核心屬性,children
表示子節點。
在前文咱們用原生dom給ul作了一些更新,如今使用Virtual Dom來實現這個過程:
針對當前的真實DOM複製一份virtual DOM,以及指望改動後的virtual DOM;
const originalDom = { tagName: "html",// 根節點 children: [ //省略中間節點 { tagName: "ul", attributes: { "class": "list" }, children: [ { tagName: "li", attributes: { "class": "list-item" }, textContent: "List item" } ] } ], } const newDom = { tagName: "html",// 根節點 children: [ //省略中間節點 { tagName: "ul", attributes: { "class": "list" }, children: [ { tagName: "li", attributes: { "class": "list-item" }, textContent: "List item one" //改動1,第一個子節點的文本 }, {// 改動2,新增了第二個節點 tagName: "li", attributes: { "class": "list-item" }, textContent: "List item two" } ] } ], };
比對差別
const diffRes = [ { newNode:{/*對應上面ul的子節點1*/}, oldNode:{/*對應上面originalUl的子節點1*/}, }, { newNode:{/*對應上面ul的子節點2*/},//這是新增節點,因此沒有oldNode }, ]
收集差別結果以後,發現只要更新list節點,,僞代碼大體以下:
const domElement = document.getElementsByClassName("list")[0]; diffRes.forEach((diff) => { const newElement = document.createElement(diff.newNode.tagName); /* Add attributes ... */ if (diff.oldNode) { // 若是存在oldNode則替換 domElement.replaceChild(diff.newNode, diff.index); } else { // 不存在則直接新增 domElement.appendChild(diff.newNode); } })
固然,實際框架諸如vue
和react
裏的diff
過程不僅是這麼簡單,它們作了更多的優化,例如:
對於有多個項的ul
,往其中append
一個新節點,可能要引發整個ul
全部節點的改動,這個改動成本過高,在diff
過程若是遇到了,可能會換一種思路來實現,直接用js生成一個新的ul
對象,而後替換原來的ul
。這些在後續介紹各個技術棧的文章(可能)會詳細介紹。
能夠看到,Virtual DOM的核心思路:先讓預期的變化操做在虛擬dom節點,最後統一應用到真實DOM中去,這個操做必定程度上減小了重繪和重排的概率,由於它作到了:
對於最後必要的dom操做,也集中在一塊兒處理,貼合瀏覽器渲染機制,減小重排次數;
如今咱們回到開篇的問題--「Virtual Dom 的優點是什麼?」
在回答這道題以前,咱們還須要知道:
而Virtual Dom 最關鍵的地方就是把dom須要作的更改,先放在js引擎裏進行運算,等收集到必定期間的全部dom變動時,這樣作的好處是:
本文從一個常見面試問題出發,介紹了Dom 和Virtual Dom的概念,以及直接操做Dom可能存在的問題,經過對比來講明Virtual Dom的優點。對於具體技術棧中的Virtual Dom diff過程和優化處理的方式,沒有作較多說明,更專一於闡述Virtual Dom自己的概念。
歡迎你們關注專欄,也但願你們對於喜好的文章,可以不吝點贊和收藏,對於行文風格和內容有任何意見的,都歡迎私信交流。