原生DOM操做vs框架虛擬DOM比較

1. 原生 DOM 操做 vs. 經過框架封裝操做。html

這是一個性能 vs. 可維護性的取捨。框架的意義在於爲你掩蓋底層的 DOM 操做,讓你用更聲明式的方式來描述你的目的,從而讓你的代碼更容易維護。沒有任何框架能夠比純手動的優化 DOM 操做更快,由於框架的 DOM 操做層須要應對任何上層 API 可能產生的操做,它的實現必須是普適的。針對任何一個 benchmark,我均可以寫出比任何框架更快的手動優化,可是那有什麼意義呢?在構建一個實際應用的時候,你難道爲每個地方都去作手動優化嗎?出於可維護性的考慮,這顯然不可能。框架給你的保證是,你在不須要手動優化的狀況下,我依然能夠給你提供過得去的性能。前端

2. 對 React 的 Virtual DOM 的誤解。vue

React 歷來沒有說過 「React 比原生操做 DOM 快」。React 的基本思惟模式是每次有變更就整個從新渲染整個應用。若是沒有 Virtual DOM,簡單來想就是直接重置 innerHTML。不少人都沒有意識到,在一個大型列表全部數據都變了的狀況下,重置 innerHTML 實際上是一個還算合理的操做... 真正的問題是在 「所有從新渲染」 的思惟模式下,即便只有一行數據變了,它也須要重置整個 innerHTML,這時候顯然就有大量的浪費。git

咱們能夠比較一下 innerHTML vs. Virtual DOM 的重繪性能消耗:github

  • innerHTML: render html string O(template size) + 從新建立全部 DOM 元素 O(DOM size)
  • Virtual DOM: render Virtual DOM + diff O(template size) + 必要的 DOM 更新 O(DOM change)

 

Virtual DOM render + diff 顯然比渲染 html 字符串要慢,可是!它依然是純 js 層面的計算,比起後面的 DOM 操做來講,依然便宜了太多。能夠看到,innerHTML 的總計算量不論是 js 計算仍是 DOM 操做都是和整個界面的大小相關,但 Virtual DOM 的計算量裏面,只有 js 計算和界面大小相關,DOM 操做是和數據的變更量相關的。前面說了,和 DOM 操做比起來,js 計算是極其便宜的。這纔是爲何要有 Virtual DOM:它保證了 1)無論你的數據變化多少,每次重繪的性能均可以接受;2) 你依然能夠用相似 innerHTML 的思路去寫你的應用。數據庫

 

做者:尤雨溪
連接:https://www.zhihu.com/question/31809713/answer/53544875
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。編程

3. MVVM vs. Virtual DOM數組

相比起 React,其餘 MVVM 系框架好比 Angular, Knockout 以及 Vue、Avalon 採用的都是數據綁定:經過 Directive/Binding 對象,觀察數據變化並保留對實際 DOM 元素的引用,當有數據變化時進行對應的操做。MVVM 的變化檢查是數據層面的,而 React 的檢查是 DOM 結構層面的。MVVM 的性能也根據變更檢測的實現原理有所不一樣:Angular 的髒檢查使得任何變更都有固定的 O(watcher count) 的代價;Knockout/Vue/Avalon 都採用了依賴收集,在 js 和 DOM 層面都是 O(change)框架

  • 髒檢查:scope digest O(watcher count) + 必要 DOM 更新 O(DOM change)
  • 依賴收集:從新收集依賴 O(data change) + 必要 DOM 更新 O(DOM change)

能夠看到,Angular 最不效率的地方在於任何小變更都有的和 watcher 數量相關的性能代價。可是!當全部數據都變了的時候,Angular 其實並不吃虧。依賴收集在初始化和數據變化的時候都須要從新收集依賴,這個代價在小量更新的時候幾乎能夠忽略,但在數據量龐大的時候也會產生必定的消耗。編輯器

MVVM 渲染列表的時候,因爲每一行都有本身的數據做用域,因此一般都是每一行有一個對應的 ViewModel 實例,或者是一個稍微輕量一些的利用原型繼承的 "scope" 對象,但也有必定的代價。因此,MVVM 列表渲染的初始化幾乎必定比 React 慢,由於建立 ViewModel / scope 實例比起 Virtual DOM 來講要昂貴不少。這裏全部 MVVM 實現的一個共同問題就是在列表渲染的數據源變更時,尤爲是當數據是全新的對象時,如何有效地複用已經建立的 ViewModel 實例和 DOM 元素。假如沒有任何複用方面的優化,因爲數據是 「全新」 的,MVVM 實際上須要銷燬以前的全部實例,從新建立全部實例,最後再進行一次渲染!這就是爲何題目裏連接的 angular/knockout 實現都相對比較慢。相比之下,React 的變更檢查因爲是 DOM 結構層面的,即便是全新的數據,只要最後渲染結果沒變,那麼就不須要作無用功。

Angular 和 Vue 都提供了列表重繪的優化機制,也就是 「提示」 框架如何有效地複用實例和 DOM 元素。好比數據庫裏的同一個對象,在兩次前端 API 調用裏面會成爲不一樣的對象,可是它們依然有同樣的 uid。這時候你就能夠提示 track by uid 來讓 Angular 知道,這兩個對象實際上是同一份數據。那麼原來這份數據對應的實例和 DOM 元素均可以複用,只須要更新變更了的部分。或者,你也能夠直接 track by $index 來進行 「原地複用」:直接根據在數組裏的位置進行復用。在題目給出的例子裏,若是 angular 實現加上 track by $index 的話,後續重繪是不會比 React 慢多少的。甚至在 dbmonster 測試中,Angular 和 Vue 用了 track by $index 之後都比 React 快: dbmon (注意 Angular 默認版本無優化,優化過的在下面)

順道說一句,React 渲染列表的時候也須要提供 key 這個特殊 prop,本質上和 track-by 是一回事。

 

4. 性能比較也要看場合

在比較性能的時候,要分清楚初始渲染、小量數據更新、大量數據更新這些不一樣的場合。Virtual DOM、髒檢查 MVVM、數據收集 MVVM 在不一樣場合各有不一樣的表現和不一樣的優化需求。Virtual DOM 爲了提高小量數據更新時的性能,也須要針對性的優化,好比 shouldComponentUpdate 或是 immutable data。

  • 初始渲染:Virtual DOM > 髒檢查 >= 依賴收集
  • 小量數據更新:依賴收集 >> Virtual DOM + 優化 > 髒檢查(沒法優化) > Virtual DOM 無優化
  • 大量數據更新:髒檢查 + 優化 >= 依賴收集 + 優化 > Virtual DOM(沒法/無需優化)>> MVVM 無優化

不要天真地覺得 Virtual DOM 就是快,diff 不是免費的,batching 麼 MVVM 也能作,並且最終 patch 的時候還不是要用原生 API。在我看來 Virtual DOM 真正的價值歷來都不是性能,而是它 1) 爲函數式的 UI 編程方式打開了大門;2) 能夠渲染到 DOM 之外的 backend,好比 ReactNative。

 

5. 總結

以上這些比較,更多的是對於框架開發研究者提供一些參考。主流的框架 + 合理的優化,足以應對絕大部分應用的性能需求。若是是對性能有極致需求的特殊狀況,其實應該犧牲一些可維護性採起手動優化:好比 Atom 編輯器在文件渲染的實現上放棄了 React 而採用了本身實現的 tile-based rendering;又好比在移動端須要 DOM-pooling 的虛擬滾動,不須要考慮順序變化,能夠繞過框架的內置實現本身搞一個。

做者:尤雨溪 連接:https://www.zhihu.com/question/31809713/answer/53544875 來源:知乎 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

相關文章
相關標籤/搜索