憑什么說virtual DOM是React的精髓所在

了解過react的都必定會知道 virtual DOM 的存在,不夸張的說,virtual DOM 就是 react 最核心的技術。virtual DOM 就如一個征戰南北的猛將,為王打下了如今前端領域的半片江山。如果你想了解 react 王朝,那么必須先了解 virtual DOM,了解這個王朝幾乎所有的生命力和戰斗力所在。

有人會覺得我夸張了,認為即使不懂 virtual DOM,也照樣可以用react來開發應用。是的沒錯,但這并不能否認 virtual DOM 的重要性,事實上,你使用 react 的時候,之所以能夠得心應手地開發著大型應用,而不用瞻前顧后地考慮著性能問題,功勞依然來自于 virtual DOM。

virtual DOM

性能

大多數人對 virtual DOM 的認知,不外乎:

  • 在瀏覽器內存中維護著的一棵與頁面 DOM 結構一致的對象樹
  • 依靠 diff 算法極大提高了 DOM 操作的性能

但并不知道 virtual DOM 是如何提高性能的。我們暫且拋開這個問題,先來了解一下瀏覽器是怎么將DOM反映到頁面上的。

瀏覽器工作流

創建 DOM 樹

一旦瀏覽器接收到一個 HTML 文件,渲染引擎(render engine)就開始解析它,并根據 HTML 元素(elements)一一對應地生成 DOM 節點(nodes),組成一棵 DOM 樹。

創建渲染樹

同時,瀏覽器也會解析來自外部 CSS 文件和元素上的 inline 樣式。在這個過程中,瀏覽器會逐步對各個節點計算最終樣式,并為包含樣式信息的 DOM 樹上的節點,再創建另外一個樹,一般被稱作渲染樹(render tree)。

布局

構造了渲染樹以后,瀏覽器引擎開始著手布局(layout)。布局時,渲染樹上的每個節點根據其在屏幕上應該出現的精確位置,分配一組屏幕坐標值。

繪制

接著,瀏覽器將會通過遍歷渲染樹,調用每個節點的 paint 方法來繪制節點在渲染樹創建階段返回的 render 對象。通過繪制,最終將在屏幕上展示內容。

性能瓶頸

從上邊瀏覽器的工作流可以看出,每一次的 DOM 操作,都會引發一次從創建 DOM 樹、創建渲染樹、布局到繪制的全過程,尤其是在創建渲染樹階段,對節點樣式的計算量通常很大。而正常的,由用戶引發的頁面改變往往不止一次的 DOM 操作,多次計算,將導致頁面性能大幅降低。

virtual DOM 做了什么

通過分析,我們可以很清楚的意識到,多次的 DOM 操作引發的多次計算,是導致頁面性能低的主要原因。而 virtual DOM 的解決方法很簡單,批量處理 DOM 操作。virtual DOM 實際上是起了一個緩沖的作用,它將一個事件循環(event loop)中發出的 DOM 操作全部收集起來,不立即在頁面上產生效果,而是在事件循環的結尾,才向頁面作用,從而合并多次的 DOM 操作為一次計算。

在此基礎上,virtual DOM 通過 Diff 算法,以優化的策略計算出最小的差別,并作用到真實的DOM上。

通過合并 DOM 操作和diff算法,virtual DOM 有效地解決了 DOM 操作所帶來的性能問題,使得 react 在開發大型復雜的單頁面應用中脫穎而出,大放異彩。

獨特的Diff算法

為什么還需要 Diff 算法呢?這是因為在 web 頁面中,DOM 樹結構通常比較穩定,對于其中某個或某幾個 DOM 節點的修改,沒必要重新創建一顆 DOM 樹。通過 Diff 算法,react將一次計算中多余的渲染工作盡最大化去除,從而進一步提升了頁面性能。

實際上,Diff 算法不是react首創,但卻是在 react 這里得到了突破性的優化。傳統標準的 Diff 算法復雜度達到了 O(n^3),這就意味著,如果要展示1000個節點,就要依次執行上十億次的比較。這是絕對無法滿足性能需求的。而 react 開發團隊通過制定大膽的策略,使得 Diff 算法復雜度降到 O(n)。

策略之所以大膽,是因為算法有所冒險。react的Diff算法是基于以下三個現實策略進行優化的:

1、Web UI 中 DOM 節點跨層級的移動操作特別少,可以忽略不計;
2、擁有相同類的兩個組件將會生成相似的樹形結構,擁有不同類的兩個組件將會生成不同的樹形結構;
3、對于同一層級的一組子節點,它們可以通過唯一 id 進行區分。

基于以上三個前提策略,React 分別對 tree diff、component diff 以及 element diff 進行算法優化,事實也證明這三個前提策略是合理且準確的,它保證了整體界面構建的性能。

tree diff

基于策略一,React 對樹的算法進行了簡潔明了的優化,即對樹進行分層比較,兩棵樹只會對同一層次的節點進行比較。
既然 DOM 節點跨層級的移動操作少到可以忽略不計,針對這一現象,React 通過對 Virtual DOM 樹進行層級控制,只會對同一個父節點下的所有子節點進行比較。當發現節點已經不存在,則該節點及其子節點會被完全刪除掉,不會用于進一步的比較。這樣只需要對樹進行一次遍歷,便能完成整個 DOM 樹的比較。

當然,這個策略的風險性就在于,當發生 DOM 節點跨層級的移動操作時,react的處理方式將極其殘暴,他會先創建新的節點,再刪除原來需要移動的節點。因此,react 官方也建議不要進行 DOM 節點跨層級的操作。

component diff

React 是基于組件構建應用的,對于組件間的比較所采取的策略也是簡潔高效。

  • 如果是同一類型的組件,按照原策略繼續比較 virtual DOM tree。
  • 如果不是,則將該組件判斷為 dirty component,從而替換整個組件下的所有子節點。
  • 對于同一類型的組件,有可能其 Virtual DOM 沒有任何變化,如果能夠確切的知道這點那可以節省大量的 diff 運算時間,因此 React 允許用戶通過 shouldComponentUpdate() 來判斷該組件是否需要進行 diff,而這個 API 也成為了 react 性能優化的常見手段。
element diff

當節點處于同一層級時,React diff 提供了三種節點操作,分別為:INSERT_MARKUP(插入)、MOVE_EXISTING(移動)和 REMOVE_NODE(刪除)。

  • INSERT_MARKUP,新的 component 類型不在老集合里, 即是全新的節點,需要對新節點執行插入操作。
  • MOVE_EXISTING,在老集合有新 component 類型,且 element 是可更新的類型,generateComponentChildren 已調用 receiveComponent,這種情況下 prevChild=nextChild,就需要做移動操作,可以復用以前的 DOM 節點。
  • REMOVE_NODE,老 component 類型,在新集合里也有,但對應的 element 不同則不能直接復用和更新,需要執行刪除操作,或者老 component 不在新集合里的,也需要執行刪除操作。

值得注意的是,react的性能優化并不是什么神秘的事,任何項目都可以運用類似方法去改善頁面性能,只不過,react幫你做了這些繁瑣的工作。

抽象

到此,我們了解了 virtual DOM 對性能的優化方案,也足以意識到高性能作為 react 的王牌優勢,virtual DOM 在其中所扮演的重要角色。但是如果談到 virtual DOM,你只想起 “提升性能” 這一個關鍵詞的話,那就說明你對 virtual DOM 還不夠了解,事實上,virtual DOM 最創造性最顛覆式的意義,在于抽象。

我們知道,virtual DOM 對真實 DOM 進行了一層抽象,它幫助我們去操作真實 DOM,而我們通過操作 virtual DOM 來控制頁面 UI。在使用 react 之前,我們的 js 代碼和 UI 是完全耦合的。但是 virtual DOM 強制在邏輯代碼和 UI 成分之間構建了一層隔離,使得邏輯和視圖低耦化,極大提高了代碼復用性。于是我們發現,一套邏輯,可以對應多個 UI。這對于代碼移植和維護是具有重大意義的。react native 的推出,完全說明了 virtual DOM 的顛覆性意義。


20150625202345835.png

上圖中,virtual DOM可以映射到web端、IOS端、安卓平臺,在不同環境下的不同表現,均使用了同一套業務邏輯。

這才是 virtual DOM 的靈魂所在,正如 react native 的理念——“Learn Once ,Write Anywhere”。 其實在 Vue 、Angular 2相繼推出后,react 的性能優勢已慢慢不再明顯,但是 virtual DOM 的革命性意義,依然保持著 react 在前端領域中不可撼動的地位,保 react 王朝生生不息。

末尾

在寫這篇博文之前,我已經在許多項目中反復地使用過 react 了,但我最近在思考,我到底了解不了解它。當然,答案的確認花不了三秒:不。甚至是一概不知。心血來潮地翻閱了很多疑惑之處,自覺醍醐灌頂,也分享給正在路上的各位。

參考資料

http://www.infoq.com/cn/articles/subversion-front-end-ui-development-framework-react/
http://blog.csdn.net/lihongxun945/article/details/46640503
http://blog.csdn.net/yczz/article/details/49886061
http://www.tuicool.com/articles/Ar6Zruq
http://www.cnblogs.com/mooniitt/p/6064749.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容

  • 參考文章:深度剖析:如何實現一個Virtual DOM 算法 作者:戴嘉華React中一個沒人能解釋清楚的問題——...
    waka閱讀 5,976評論 0 21
  • 一、讀懂diff diff是Unix/Linux系統的一個很重要的工具程序。它用來比較兩個文本文件的差異,是代碼版...
    overflow_hidden閱讀 1,870評論 2 2
  • Virtual DOM是React中的一個很重要的概念,在日常開發中,前端工程師們需要將后臺的數據呈現到界面中,同...
    SherHoooo閱讀 992評論 3 5
  • 本文闡述的內容: Dom操作之重繪重排 結合vue源碼理解Vitrual Dom原理 理解這一部分是為了的目的: ...
    Jmingzi_閱讀 1,574評論 4 4
  • 前言 “步入前端兩年半,自覺菜雞懶又爛。” 近來想著寫寫一些前端學習的心得,左思右想。還是從 React 入筆。為...
    唐紫依閱讀 4,803評論 2 12