Vue原理--虛擬DOM

爲何須要虛擬DOM?前端

  若是對前端工做進行抽象的話,主要就是維護狀態和更新視圖,而更新視圖和維護狀態都須要DOM操做。其實近年來,前端的框架主要發展方向就是解放DOM操做的複雜性。vue

  運行js的速度是很快的,大量的操做DOM就會很慢,時常在更新數據後會從新渲染頁面,這樣形成在沒有改變數據的地方也從新渲染了DOM 節點,這樣就形成了很大程度上的資源浪費。git

  在jQuery出現之前,咱們直接操做DOM結構,這種方法複雜度高,兼容性也較差。有了jQuery強大的選擇器以及高度封裝的API,咱們能夠更方便的操做DOM,jQuery幫咱們處理兼容性問題,同時也使DOM操做變得簡單。程序員

  可是聰明的程序員不可能知足於此,各類MVVM框架應運而生,有angularJS、avalon、vue.js等,MVVM使用數據雙向綁定,使得咱們徹底不須要操做DOM了,更新了狀態,視圖會自動更新。更新了視圖數據狀態也會自動更新,能夠說MVVM使得前端的開發效率大幅提高。可是其大量的事件綁定使得其在複雜場景下的執行性能堪憂,有沒有一種兼顧開發效率和執行效率的方案呢?由此引入Virtual DOM(虛擬DOM)github

  利用在內存中生成與真實DOM與之對應的數據結構,這個在內存中生成的結構稱之爲虛擬DOM算法

  當數據發生變化時,可以智能地計算出從新渲染組件的最小代價並應用到DOM操做上緩存

 Virtual DOM 算法數據結構

  所謂的 Virtual DOM 算法。包括幾個步驟:app

  1.用 JavaScript 對象結構表示 DOM 樹的結構;而後用這個樹構建一個真正的 DOM 樹,插到文檔當中;框架

  2.當狀態變動的時候,從新構造一棵新的對象樹。而後用新的樹和舊的樹進行比較,記錄兩棵樹差別;

  3.把2所記錄的差別應用到步驟1所構建的真正的DOM樹上,視圖就更新了。

  Virtual DOM 本質上就是在 JS 和 DOM 之間作了一個緩存。能夠類比 CPU 和硬盤,既然硬盤這麼慢,咱們就在它們之間加個緩存。

  既然 DOM 這麼慢,咱們就在它們 JS 和 DOM 之間加個緩存。CPU(JS)只操做內存(Virtual DOM),最後的時候再把變動寫入硬盤(DOM)。

 

   所謂的virtual dom,也就是虛擬節點。它經過js的Object對象模擬DOM中的節點,而後再經過特定的render方法將其渲染成真實的DOM節點 dom。diff 則是經過JS層面的計算,返回一個patch對象,即補丁對象,在經過特定的操做解析patch對象,完成頁面的從新渲染

 

 

 

比較兩棵虛擬DOM樹的差別

  比較兩棵DOM樹的差別是 Virtual DOM 算法最核心的部分,這也是所謂的 Virtual DOM 的 diff 算法

  兩個樹的徹底的 diff 算法是一個時間複雜度爲 O(n^3) 的問題。可是在前端當中,你不多會跨越層級地移動DOM元素。因此 Virtual DOM 只會對同一個層級的元素進行對比:

 

   上面的div只會和同一層級的div對比,第二層級的只會跟第二層級對比。這樣算法複雜度就能夠達到 O(n)。

  在實際的代碼中,會對新舊兩棵樹進行一個深度優先的遍歷,這樣每一個節點都會有一個惟一的標記,以下圖所示:

Virtual DOM 算法實現

  Virtual DOM 算法得實現主要是用三個函數:elementdiffpatch。而後就能夠實際的進行使用,以下面代碼所示:

 

// 1. 構建虛擬DOM
var tree = el('div', {'id': 'container'}, [
    el('h1', {style: 'color: blue'}, ['simple virtal dom']),
    el('p', ['Hello, virtual-dom']),
    el('ul', [el('li')])
])

// 2. 經過虛擬DOM構建真正的DOM
var root = tree.render()
document.body.appendChild(root)

// 3. 生成新的虛擬DOM
var newTree = el('div', {'id': 'container'}, [
    el('h1', {style: 'color: red'}, ['simple virtal dom']),
    el('p', ['Hello, virtual-dom']),
    el('ul', [el('li'), el('li')])
])

// 4. 比較兩棵虛擬DOM樹的不一樣
var patches = diff(tree, newTree)

// 5. 在真正的DOM元素上應用變動
patch(root, patches)

 

diff算法

 用 三大策略 將O(n^3)複雜度 轉化爲 O(n)複雜度

  • 策略一(tree diff):

  Web UI中DOM節點跨層級的移動操做特別少,能夠忽略不計。

  • 策略二(component diff):

  擁有相同類的兩個組件 生成類似的樹形結構,
  擁有不一樣類的兩個組件 生成不一樣的樹形結構。

  • 策略三(element diff):

  對於同一層級的一組子節點,經過惟一id區分。

 tree diff
(1)經過updateDepth對Virtual DOM樹進行層級控制。
(2)對樹分層比較,兩棵樹只對同一層次節點進行比較。若是該節點不存在時,則該節點及其子節點會被徹底刪除,不會再進一步比較。
(3)只需遍歷一次,就能完成整棵DOM樹的比較。

 

   diff只簡單考慮同層級的節點位置變換,若是是跨層級的話,只有建立節點和刪除節點的操做

 

 

      如上圖所示,以A爲根節點的整棵樹會被從新建立而不是移動,所以官方建議不要進行DOM節點跨層級操做,能夠經過CSS隱藏、顯示節點,而不是真正地移除、添加DOM節點。

相關文章
相關標籤/搜索