這件事還要從 2013 年那個秋天提及。javascript
這實際上很是快,主要是由於大多數DOM操做每每很慢。DOM上有不少性能工做,但大多數DOM操做都會丟幀。html
對!就是這張圖,這張圖把你們引入了 DOM
操做是昂貴且慢的,Virtual DOM
是快速的思惟裏。前端
6 年後的今天,React
已經風靡全球,Virtual DOM
也受到了你們的承認,國產之星 VUE
也使用了 Virtual DOM
。java
那麼問題來了,Virtual DOM
真的快嗎?Virtual DOM
的意義究竟是什麼?咱們爲何要使用 Virtual DOM
?react
咱們都據說直接更新文檔對象模型(DOM
)效率低且速度慢。可是,咱們中不多有人真的有數據支持它。關於React虛擬DOM的討論是,它是一種更有效的方式來更新 Web
應用程序中的視圖,但咱們不多有人知道爲何以及這種效率是否會致使更快的頁面渲染時間。git
拋開使用 React
的其餘好處,例如單向數據綁定和組件,我將討論 Virtual DOM
究竟是什麼,以及它是否可以證實 React
比其餘UI庫更合理(或者根本沒有UI庫) 。github
咱們爲何須要 UI 庫呢?算法
我敢確定,如今的前端界,很大一部分人離開了三大框架以後就不知道該怎麼辦了,他們可能理所固然的認爲視圖和數據是綁定的(VUE),或者直接使用 setState
來更新視圖(React)。npm
有了 UI
庫以後,咱們能夠直接數據與視圖綁定,而不須要再操做 DOM
。瀏覽器
這裏不會詳細的講 DOM
,只會粗略帶過。
DOM
表明文檔對象模型,是結構化文本的抽象。對於Web
開發人員,此文本是HTML
代碼,DOM
簡稱爲*HTML DOM
。HTML
的元素成爲DOM
中的節點*。
HTML DOM
提供了一個用於遍歷和修改節點的接口(API)。它包含像getElementById
或的方法removeChild
。咱們一般使用JavaScript
語言來處理DOM
,由於......好吧,沒人知道爲何:)。
所以,每當咱們想要動態地更改網頁的內容時,咱們都會修改DOM
:
var item = document.getElementById("myLI");
item.parentNode.removeChild(item);
複製代碼
document
是根節點的抽象getElementById
,parentNode
並且removeChild
是來自HTML DOM API
的方法。
那麼問題來了,因爲HTML DOM
始終是樹形結構,咱們能夠很容易地遍歷每一個節點,可是現在Web APP
的當下,DOM
樹愈來愈大,咱們須要不停的修改大量的DOM
樹。這是真正使人痛苦的地方。
咱們一般是如下一個流程來更新 DOM
:
這明顯有幾個問題:
bug
。更新DOM
並不慢,就像更新任何JavaScript
對象同樣。
那到底是什麼讓更新真正的DOM
變慢?
是繪製。
佈局過程當中,繪製佔用了大部分時間。
結合下圖,以及此文章,你會明白,更新 DOM
的真正問題是屏幕的繪製。
負責在瀏覽器屏幕上顯示或呈現網頁的渲染引擎解析HTML
頁面以建立DOM
。它還解析CSS
並將CSS
應用於HTML
,從而建立渲染樹,此過程稱爲**attachment
**。
因此,當咱們這樣作時
document.getElementById('elementId').innerHTML="New Value"
複製代碼
發生如下事情:
HTML
elementId
的子元素DOM
CSS
從新計算CSS和更改佈局使用複雜的算法,它們會影響性能。
所以,更新真正的DOM
並不只僅涉及更新DOM
,而是涉及許多其餘過程。
此外,上述每一個步驟都針對真實DOM
的每次更新運行,即若是咱們更新真實DOM
10次,則上述步驟中的每個將重複10次。這就是爲何更新 DOM
很慢的緣由。
首先 , Virtual DOM
不是由 React
發明的,但React
使用它並免費提供。
因爲 DOM
操做的複雜性,Virtual DOM
被創造了出來,他以一個虛擬樹的狀態,存儲在內存中,再映射到真實的 DOM
,每次更新,都是虛擬樹的對比,再將差別部分進行更新,並反映到真實 DOM
上去,這樣咱們就減小了對真實 DOM
的操做。
在React
中更新虛擬DOM
的速度更快,由於React
使用了
observable
)而不是髒檢查來檢測更改AngularJS
使用髒檢查來查找已更改的模型。這個髒檢查過程在指定時間後循環運行。隨着應用程序的增加,檢查整個模型會下降性能,從而使應用程序變慢。
每當調用setState()
方法時,ReactJS
都會從頭開始建立整個Virtual DOM
。建立整棵樹很是快,所以不會影響性能。
在任何給定時間,ReactJS
維護兩個Virtual DOM
,一個具備更新的狀態Virtual DOM
,另外一個具備先前的狀態Virtual DOM
。
使用diff算法比較Virtual DOM
以找到並更新至Real DOM
。
讓咱們舉個栗子,首先咱們 state.subject
的值是 world
:
<div>
<div id="header">
<h1>Hello, {{state.subject}}!</h1>
<p>How are you today?</p>
</div>
</div>
複製代碼
解析後的 Virtual DOM
能夠表示爲:
{
tag: 'div',
children: [
{
tag: 'div',
attributes: {
id: 'header'
},
children: [
{
tag: 'h1',
children: 'Hello, World!'
},
{
tag: 'p',
children: 'How are you today?'
}
]
}
]
}
複製代碼
如今, state.subject
的值改變爲Mom
,那麼渲染出來的 Virtual DOM
爲:
{
tag: 'div',
children: [
{
tag: 'div',
attributes: {
id: 'header'
},
children: [
{
tag: 'h1',
children: 'Hello, Mom!'
},
{
tag: 'p',
children: 'How are you today?'
}
]
}
]
}
複製代碼
經過 diff 算法以後,肯定只更新的了 h1
這個元素,那麼將更新的元素再映射到 DOM
上即完成了這次的更新。
至於 batching
和 diff 算法
,內容量較大,須要另開一篇博客講,目前能搜到的講解也很多,你們能夠去搜搜。
關於Virtual DOM
的每一篇文章和文章都會指出,雖然今天的JavaScript
引擎速度很是快,但讀取和寫入瀏覽器的DOM
的速度很慢。
這不徹底正確。DOM
很快。添加和刪除DOM
節點並不比在JavaScript
對象上設置屬性慢得多。這只是一個簡單的操做。
例以下面一個例子:
這是一個使用原生 DOM
渲染的方式:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello JavaScript!</title>
</head>
<body>
<div id="example"></div>
<script> document.getElementById("example").innerHTML = "<h1>Hello, world!</h1>"; </script>
</body>
</html>
複製代碼
這是一個使用 React
實現的方式:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="build/react.js"></script>
<script src="build/react-dom.js"></script>
</script>
</head>
<body>
<div id="example"></div>
<script type="text/babel"> ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') ); </script>
</body>
</html>
複製代碼
使用原生須要渲染的時間:
使用 React
須要渲染的時間:
咋一看,原生渲染速度大大快於 React
。
可是咱們忽略了一個問題,就是頁面數據量不多,這樣操做,在一個大型列表全部數據都變了的狀況下,還算是合理,可是,當只有一行數據發生變化時,它也須要重置整個 innerHTML
,這時候顯然就形成了大量浪費。
比較 innerHTML
和 Virtual DOM
的重繪過程以下:
innerHTML
: render html string ==> 從新建立全部 DOM 元素Virtual DOM
: render Virtual DOM ==> diff ==> 必要的 DOM 更新和 DOM
操做比起來,js
計算是很是廉價的。Virtual DOM render
+ diff
顯然比渲染 html
字符串要慢,可是,它依然是純 js
層面的計算,比起後面的 DOM
操做來講,依然好了太多。
瀏覽器在DOM
更改時必須執行的佈局。每次DOM
更改時,瀏覽器都須要從新計算CSS
,進行佈局並從新繪製網頁,這須要大量時間。
瀏覽器製造商不斷努力縮短從新繪製屏幕所需的時間,能夠作的最大的事情是最小化和批量DOM
更改。
這種減小和批處理DOM
更改的策略,採用另外一個抽象級別,是React
的Virtual DOM
背後的理念。
React
歷來沒有說過 「React
比原生操做 DOM
快」。React
給咱們的保證是,在不須要手動優化的狀況下,它依然能夠給咱們提供過得去的性能。
React
掩蓋了底層的 DOM
操做,能夠用更聲明式的方式來描述咱們目的,從而讓代碼更容易維護。
借鑑了知乎上的回答:沒有任何框架能夠比純手動的優化 DOM
操做更快,由於框架的 DOM
操做層須要應對任何上層 API
可能產生的操做,它的實現必須是普適的。針對任何一個 benchmark
,我均可以寫出比任何框架更快的手動優化,可是那有什麼意義呢?在構建一個實際應用的時候,你難道爲每個地方都去作手動優化嗎?出於可維護性的考慮,這顯然不可能。
最後推廣一下我基於 Taro
框架寫的組件庫:MP-ColorUI。
能夠順手 star 一下我就很開心啦,謝謝你們。