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

了解過react的都必定會(huì)知道 virtual DOM 的存在熔恢,不夸張的說个粱,virtual DOM 就是 react 最核心的技術(shù)撵彻。virtual DOM 就如一個(gè)征戰(zhàn)南北的猛將墙贱,為王打下了如今前端領(lǐng)域的半片江山艺演。如果你想了解 react 王朝却紧,那么必須先了解 virtual DOM,了解這個(gè)王朝幾乎所有的生命力和戰(zhàn)斗力所在胎撤。

有人會(huì)覺得我夸張了晓殊,認(rèn)為即使不懂 virtual DOM,也照樣可以用react來開發(fā)應(yīng)用伤提。是的沒錯(cuò)巫俺,但這并不能否認(rèn) virtual DOM 的重要性,事實(shí)上肿男,你使用 react 的時(shí)候介汹,之所以能夠得心應(yīng)手地開發(fā)著大型應(yīng)用,而不用瞻前顧后地考慮著性能問題次伶,功勞依然來自于 virtual DOM痴昧。

virtual DOM

性能

大多數(shù)人對(duì) virtual DOM 的認(rèn)知,不外乎:

  • 在瀏覽器內(nèi)存中維護(hù)著的一棵與頁面 DOM 結(jié)構(gòu)一致的對(duì)象樹
  • 依靠 diff 算法極大提高了 DOM 操作的性能

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

瀏覽器工作流

創(chuàng)建 DOM 樹

一旦瀏覽器接收到一個(gè) HTML 文件柱彻,渲染引擎(render engine)就開始解析它,并根據(jù) HTML 元素(elements)一一對(duì)應(yīng)地生成 DOM 節(jié)點(diǎn)(nodes)餐胀,組成一棵 DOM 樹哟楷。

創(chuàng)建渲染樹

同時(shí),瀏覽器也會(huì)解析來自外部 CSS 文件和元素上的 inline 樣式否灾。在這個(gè)過程中卖擅,瀏覽器會(huì)逐步對(duì)各個(gè)節(jié)點(diǎn)計(jì)算最終樣式,并為包含樣式信息的 DOM 樹上的節(jié)點(diǎn),再創(chuàng)建另外一個(gè)樹惩阶,一般被稱作渲染樹(render tree)挎狸。

布局

構(gòu)造了渲染樹以后,瀏覽器引擎開始著手布局(layout)断楷。布局時(shí)锨匆,渲染樹上的每個(gè)節(jié)點(diǎn)根據(jù)其在屏幕上應(yīng)該出現(xiàn)的精確位置,分配一組屏幕坐標(biāo)值冬筒。

繪制

接著恐锣,瀏覽器將會(huì)通過遍歷渲染樹,調(diào)用每個(gè)節(jié)點(diǎn)的 paint 方法來繪制節(jié)點(diǎn)在渲染樹創(chuàng)建階段返回的 render 對(duì)象舞痰。通過繪制土榴,最終將在屏幕上展示內(nèi)容。

性能瓶頸

從上邊瀏覽器的工作流可以看出响牛,每一次的 DOM 操作玷禽,都會(huì)引發(fā)一次從創(chuàng)建 DOM 樹、創(chuàng)建渲染樹娃善、布局到繪制的全過程论衍,尤其是在創(chuàng)建渲染樹階段,對(duì)節(jié)點(diǎn)樣式的計(jì)算量通常很大聚磺。而正常的坯台,由用戶引發(fā)的頁面改變往往不止一次的 DOM 操作,多次計(jì)算瘫寝,將導(dǎo)致頁面性能大幅降低蜒蕾。

virtual DOM 做了什么

通過分析,我們可以很清楚的意識(shí)到焕阿,多次的 DOM 操作引發(fā)的多次計(jì)算咪啡,是導(dǎo)致頁面性能低的主要原因。而 virtual DOM 的解決方法很簡(jiǎn)單暮屡,批量處理 DOM 操作撤摸。virtual DOM 實(shí)際上是起了一個(gè)緩沖的作用,它將一個(gè)事件循環(huán)(event loop)中發(fā)出的 DOM 操作全部收集起來褒纲,不立即在頁面上產(chǎn)生效果准夷,而是在事件循環(huán)的結(jié)尾,才向頁面作用莺掠,從而合并多次的 DOM 操作為一次計(jì)算衫嵌。

在此基礎(chǔ)上,virtual DOM 通過 Diff 算法彻秆,以優(yōu)化的策略計(jì)算出最小的差別楔绞,并作用到真實(shí)的DOM上结闸。

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

獨(dú)特的Diff算法

為什么還需要 Diff 算法呢耻讽?這是因?yàn)樵?web 頁面中察纯,DOM 樹結(jié)構(gòu)通常比較穩(wěn)定,對(duì)于其中某個(gè)或某幾個(gè) DOM 節(jié)點(diǎn)的修改针肥,沒必要重新創(chuàng)建一顆 DOM 樹饼记。通過 Diff 算法,react將一次計(jì)算中多余的渲染工作盡最大化去除慰枕,從而進(jìn)一步提升了頁面性能具则。

實(shí)際上,Diff 算法不是react首創(chuàng)具帮,但卻是在 react 這里得到了突破性的優(yōu)化博肋。傳統(tǒng)標(biāo)準(zhǔn)的 Diff 算法復(fù)雜度達(dá)到了 O(n^3),這就意味著蜂厅,如果要展示1000個(gè)節(jié)點(diǎn)匪凡,就要依次執(zhí)行上十億次的比較。這是絕對(duì)無法滿足性能需求的掘猿。而 react 開發(fā)團(tuán)隊(duì)通過制定大膽的策略病游,使得 Diff 算法復(fù)雜度降到 O(n)。

策略之所以大膽稠通,是因?yàn)樗惴ㄓ兴半U(xiǎn)衬衬。react的Diff算法是基于以下三個(gè)現(xiàn)實(shí)策略進(jìn)行優(yōu)化的:

1、Web UI 中 DOM 節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作特別少改橘,可以忽略不計(jì)滋尉;
2、擁有相同類的兩個(gè)組件將會(huì)生成相似的樹形結(jié)構(gòu)飞主,擁有不同類的兩個(gè)組件將會(huì)生成不同的樹形結(jié)構(gòu)狮惜;
3、對(duì)于同一層級(jí)的一組子節(jié)點(diǎn)碌识,它們可以通過唯一 id 進(jìn)行區(qū)分讽挟。

基于以上三個(gè)前提策略,React 分別對(duì) tree diff丸冕、component diff 以及 element diff 進(jìn)行算法優(yōu)化,事實(shí)也證明這三個(gè)前提策略是合理且準(zhǔn)確的薛窥,它保證了整體界面構(gòu)建的性能胖烛。

tree diff

基于策略一眼姐,React 對(duì)樹的算法進(jìn)行了簡(jiǎn)潔明了的優(yōu)化,即對(duì)樹進(jìn)行分層比較佩番,兩棵樹只會(huì)對(duì)同一層次的節(jié)點(diǎn)進(jìn)行比較众旗。
既然 DOM 節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作少到可以忽略不計(jì),針對(duì)這一現(xiàn)象趟畏,React 通過對(duì) Virtual DOM 樹進(jìn)行層級(jí)控制贡歧,只會(huì)對(duì)同一個(gè)父節(jié)點(diǎn)下的所有子節(jié)點(diǎn)進(jìn)行比較。當(dāng)發(fā)現(xiàn)節(jié)點(diǎn)已經(jīng)不存在赋秀,則該節(jié)點(diǎn)及其子節(jié)點(diǎn)會(huì)被完全刪除掉利朵,不會(huì)用于進(jìn)一步的比較。這樣只需要對(duì)樹進(jìn)行一次遍歷猎莲,便能完成整個(gè) DOM 樹的比較绍弟。

當(dāng)然,這個(gè)策略的風(fēng)險(xiǎn)性就在于著洼,當(dāng)發(fā)生 DOM 節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作時(shí)樟遣,react的處理方式將極其殘暴,他會(huì)先創(chuàng)建新的節(jié)點(diǎn)身笤,再刪除原來需要移動(dòng)的節(jié)點(diǎn)豹悬。因此,react 官方也建議不要進(jìn)行 DOM 節(jié)點(diǎn)跨層級(jí)的操作液荸。

component diff

React 是基于組件構(gòu)建應(yīng)用的瞻佛,對(duì)于組件間的比較所采取的策略也是簡(jiǎn)潔高效。

  • 如果是同一類型的組件莹弊,按照原策略繼續(xù)比較 virtual DOM tree涤久。
  • 如果不是,則將該組件判斷為 dirty component忍弛,從而替換整個(gè)組件下的所有子節(jié)點(diǎn)响迂。
  • 對(duì)于同一類型的組件,有可能其 Virtual DOM 沒有任何變化细疚,如果能夠確切的知道這點(diǎn)那可以節(jié)省大量的 diff 運(yùn)算時(shí)間蔗彤,因此 React 允許用戶通過 shouldComponentUpdate() 來判斷該組件是否需要進(jìn)行 diff,而這個(gè) API 也成為了 react 性能優(yōu)化的常見手段疯兼。
element diff

當(dāng)節(jié)點(diǎn)處于同一層級(jí)時(shí)然遏,React diff 提供了三種節(jié)點(diǎn)操作,分別為:INSERT_MARKUP(插入)吧彪、MOVE_EXISTING(移動(dòng))和 REMOVE_NODE(刪除)待侵。

  • INSERT_MARKUP,新的 component 類型不在老集合里姨裸, 即是全新的節(jié)點(diǎn)秧倾,需要對(duì)新節(jié)點(diǎn)執(zhí)行插入操作怨酝。
  • MOVE_EXISTING,在老集合有新 component 類型那先,且 element 是可更新的類型农猬,generateComponentChildren 已調(diào)用 receiveComponent,這種情況下 prevChild=nextChild售淡,就需要做移動(dòng)操作斤葱,可以復(fù)用以前的 DOM 節(jié)點(diǎn)。
  • REMOVE_NODE揖闸,老 component 類型揍堕,在新集合里也有,但對(duì)應(yīng)的 element 不同則不能直接復(fù)用和更新楔壤,需要執(zhí)行刪除操作鹤啡,或者老 component 不在新集合里的,也需要執(zhí)行刪除操作蹲嚣。

值得注意的是递瑰,react的性能優(yōu)化并不是什么神秘的事,任何項(xiàng)目都可以運(yùn)用類似方法去改善頁面性能隙畜,只不過抖部,react幫你做了這些繁瑣的工作。

抽象

到此议惰,我們了解了 virtual DOM 對(duì)性能的優(yōu)化方案慎颗,也足以意識(shí)到高性能作為 react 的王牌優(yōu)勢(shì),virtual DOM 在其中所扮演的重要角色言询。但是如果談到 virtual DOM俯萎,你只想起 “提升性能” 這一個(gè)關(guān)鍵詞的話,那就說明你對(duì) virtual DOM 還不夠了解运杭,事實(shí)上夫啊,virtual DOM 最創(chuàng)造性最顛覆式的意義,在于抽象辆憔。

我們知道撇眯,virtual DOM 對(duì)真實(shí) DOM 進(jìn)行了一層抽象,它幫助我們?nèi)ゲ僮髡鎸?shí) DOM虱咧,而我們通過操作 virtual DOM 來控制頁面 UI熊榛。在使用 react 之前,我們的 js 代碼和 UI 是完全耦合的腕巡。但是 virtual DOM 強(qiáng)制在邏輯代碼和 UI 成分之間構(gòu)建了一層隔離玄坦,使得邏輯和視圖低耦化,極大提高了代碼復(fù)用性绘沉。于是我們發(fā)現(xiàn)营搅,一套邏輯云挟,可以對(duì)應(yīng)多個(gè) UI。這對(duì)于代碼移植和維護(hù)是具有重大意義的转质。react native 的推出,完全說明了 virtual DOM 的顛覆性意義帖世。


20150625202345835.png

上圖中休蟹,virtual DOM可以映射到web端、IOS端日矫、安卓平臺(tái)赂弓,在不同環(huán)境下的不同表現(xiàn),均使用了同一套業(yè)務(wù)邏輯哪轿。

這才是 virtual DOM 的靈魂所在盈魁,正如 react native 的理念——“Learn Once ,Write Anywhere”。 其實(shí)在 Vue 窃诉、Angular 2相繼推出后杨耙,react 的性能優(yōu)勢(shì)已慢慢不再明顯,但是 virtual DOM 的革命性意義飘痛,依然保持著 react 在前端領(lǐng)域中不可撼動(dòng)的地位珊膜,保 react 王朝生生不息。

末尾

在寫這篇博文之前宣脉,我已經(jīng)在許多項(xiàng)目中反復(fù)地使用過 react 了车柠,但我最近在思考,我到底了解不了解它塑猖。當(dāng)然竹祷,答案的確認(rèn)花不了三秒:不。甚至是一概不知羊苟。心血來潮地翻閱了很多疑惑之處塑陵,自覺醍醐灌頂,也分享給正在路上的各位践险。

參考資料

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末猿妈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子巍虫,更是在濱河造成了極大的恐慌彭则,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件占遥,死亡現(xiàn)場(chǎng)離奇詭異俯抖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瓦胎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門芬萍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尤揣,“玉大人,你說我怎么就攤上這事柬祠”毕罚” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵漫蛔,是天一觀的道長(zhǎng)嗜愈。 經(jīng)常有香客問我,道長(zhǎng)莽龟,這世上最難降的妖魔是什么蠕嫁? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮毯盈,結(jié)果婚禮上剃毒,老公的妹妹穿的比我還像新娘。我一直安慰自己搂赋,他們只是感情好赘阀,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著厂镇,像睡著了一般纤壁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捺信,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天酌媒,我揣著相機(jī)與錄音,去河邊找鬼迄靠。 笑死秒咨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掌挚。 我是一名探鬼主播雨席,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吠式!你這毒婦竟也來了陡厘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤特占,失蹤者是張志新(化名)和其女友劉穎糙置,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體是目,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谤饭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揉抵。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡亡容,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冤今,到底是詐尸還是另有隱情闺兢,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布辟汰,位于F島的核電站列敲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏帖汞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一凑术、第九天 我趴在偏房一處隱蔽的房頂上張望翩蘸。 院中可真熱鬧,春花似錦淮逊、人聲如沸催首。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽郎任。三九已至,卻和暖如春备籽,著一層夾襖步出監(jiān)牢的瞬間舶治,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工车猬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留霉猛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓珠闰,卻偏偏與公主長(zhǎng)得像惜浅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子伏嗜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 參考文章:深度剖析:如何實(shí)現(xiàn)一個(gè)Virtual DOM 算法 作者:戴嘉華React中一個(gè)沒人能解釋清楚的問題——...
    waka閱讀 5,951評(píng)論 0 21
  • 一坛悉、讀懂diff diff是Unix/Linux系統(tǒng)的一個(gè)很重要的工具程序。它用來比較兩個(gè)文本文件的差異承绸,是代碼版...
    overflow_hidden閱讀 1,847評(píng)論 2 2
  • Virtual DOM是React中的一個(gè)很重要的概念裸影,在日常開發(fā)中,前端工程師們需要將后臺(tái)的數(shù)據(jù)呈現(xiàn)到界面中八酒,同...
    SherHoooo閱讀 976評(píng)論 3 5
  • 本文闡述的內(nèi)容: Dom操作之重繪重排 結(jié)合vue源碼理解Vitrual Dom原理 理解這一部分是為了的目的: ...
    Jmingzi_閱讀 1,558評(píng)論 4 4
  • 前言 “步入前端兩年半空民,自覺菜雞懶又爛。” 近來想著寫寫一些前端學(xué)習(xí)的心得界轩,左思右想画饥。還是從 React 入筆。為...
    唐紫依閱讀 4,783評(píng)論 2 12