譯者:TinkGu
鏈接:http://www.zcfy.cc/article/1211
原文:https://hashnode.com/post/the-one-thing-that-no-one-properly-explains-about-react-why-virtual-dom-cisczhfj41bmssp53mvfwmgrq
有一天波材,我的朋友向我提了一個(gè)有關(guān)React的問題:
組件化, 單向數(shù)據(jù)綁定,這些我都懂了。但是React為什么要用Virtual DOM呢霹购?
我的回答非常套路,“因?yàn)橹苯硬僮鱀OM比較低效融欧,比較慢咱圆。”羽利。
“但是現(xiàn)在的js引擎總是搞個(gè)大新聞宫患,說(shuō)自己的性能比之前又要不知道高到哪里去了。既然如此这弧,為什么還會(huì)說(shuō)直接操作DOM比較慢呢娃闲?”
好吧... 這確實(shí)是一個(gè)好問題。
驚人的是匾浪,我找了半天皇帮,發(fā)現(xiàn)并沒有任何一篇文章可以給出堅(jiān)如磐石的證明,來(lái)完滿地解釋Virtual DOM的必要性蛋辈。
其實(shí)属拾,使得整個(gè)流程變得低效的,并不只有直接操作DOM冷溶,還包括了操作DOM之后發(fā)生的事情渐白。
為了能讓你更好地理解Virtual DOM的必要性,我們先來(lái)個(gè)急轉(zhuǎn)彎逞频,從宏觀上來(lái)看瀏覽器的工作流纯衍。以及,一次DOM更新后苗胀,到底會(huì)發(fā)生什么事呢襟诸?
瀏覽器工作流
NOTE:在下面這張圖中,配圖文字使用的是Webkit引擎的術(shù)語(yǔ)基协。所有的瀏覽器都是遵循類似的工作流歌亲,僅在細(xì)節(jié)處略有不同。
創(chuàng)建DOM樹
- 一旦瀏覽器接收到一個(gè)HTML文件堡掏,渲染引擎(
render engine
)就開始解析它应结,并根據(jù)HTML元素(elements
)一一對(duì)應(yīng)地生成DOM 節(jié)點(diǎn)(nodes
),組成一棵DOM樹泉唁。
創(chuàng)建渲染樹
- 同時(shí)鹅龄,瀏覽器也會(huì)解析來(lái)自外部CSS文件和元素上的inline樣式。通常瀏覽器會(huì)為這些樣式信息亭畜,連同包含樣式信息的DOM樹上的節(jié)點(diǎn)扮休,再創(chuàng)建另外一個(gè)樹,一般被稱作渲染樹(
render tree
)
創(chuàng)建渲染樹背后的故事
- WebKit內(nèi)核的瀏覽器上拴鸵,處理一個(gè)節(jié)點(diǎn)的樣式的過程稱為
attachment
玷坠。DOM樹上的每個(gè)節(jié)點(diǎn)都有一個(gè)attach
方法蜗搔,它接收計(jì)算好的樣式信息,返回一個(gè)render
對(duì)象(又名renderer
) Attachment
的過程是同步的八堡,新節(jié)點(diǎn)插入DOM樹時(shí)樟凄,會(huì)調(diào)用新節(jié)點(diǎn)的attach
方法。- 構(gòu)建渲染樹時(shí)兄渺,由于包含了這些
render
對(duì)象缝龄,每個(gè)render
對(duì)象都需要計(jì)算視覺屬性(visual properties
);這個(gè)過程通過計(jì)算每個(gè)元素的樣式屬性來(lái)完成挂谍。
布局 Layout
又被簡(jiǎn)稱為Reflow
[2]
- 構(gòu)造了渲染樹以后叔壤,瀏覽器引擎開始著手布局(
layout
)。布局時(shí)口叙,渲染樹上的每個(gè)節(jié)點(diǎn)根據(jù)其在屏幕上應(yīng)該出現(xiàn)的精確位置炼绘,分配一組屏幕坐標(biāo)值。
繪制 Painting
- 接著妄田,瀏覽器將會(huì)通過遍歷渲染樹俺亮,調(diào)用每個(gè)節(jié)點(diǎn)的
paint
方法來(lái)繪制這些render對(duì)象。paint
方法根據(jù)瀏覽器平臺(tái)形庭,使用不同的UI后端API(agnostic UI backend API
)铅辞。
通過繪制厌漂,最終將在屏幕上展示內(nèi)容萨醒。
再來(lái)看Virtual DOM
好啦,現(xiàn)在你已經(jīng)簡(jiǎn)單過了一遍瀏覽器引擎的渲染流程苇倡,你可以看到富纸,從創(chuàng)建渲染樹,到布局旨椒,一直到繪制晓褪,只要你在這過程中進(jìn)行一次DOM更新,整個(gè)渲染流程都會(huì)重做一遍综慎。尤其是創(chuàng)建渲染樹涣仿,它需要重新計(jì)算所有元素上的所有樣式。
在一個(gè)復(fù)雜的單頁(yè)面應(yīng)用中示惊,經(jīng)常會(huì)涉及到大量的DOM操作好港,這將引起多次計(jì)算,使得整個(gè)流程變得低效米罚,這應(yīng)該盡量避免钧汹。
Virtual DOM這個(gè)抽象層真正的閃光點(diǎn)正在于此:每當(dāng)你想對(duì)視圖進(jìn)行一次更新,那些本該直接作用于真實(shí)DOM的改動(dòng)录择,都會(huì)先作用于Virtual DOM拔莱,然后再將要改動(dòng)的部分通知到真實(shí)DOM碗降。這樣可以大幅減少DOM操作帶來(lái)的重計(jì)算步驟。
Update: Reddit上的 ugwe43to874nf4 對(duì)Virtual DOM的重要性做了更客觀的評(píng)價(jià)塘秦。
DOM 操作 真正的問題在于每次操作都會(huì)觸發(fā)布局的改變讼渊、DOM樹的修改和渲染。所以尊剔,當(dāng)你一個(gè)接一個(gè)地去修改30個(gè)節(jié)點(diǎn)的時(shí)候精偿,就會(huì)引起30次(潛在的)布局重算,30次(潛在的)重繪赋兵,等等笔咽。
Virtual DOM 實(shí)際上沒有使用什么全新的技術(shù),僅僅是把 “ 雙緩沖(
double buffering
)” 技術(shù)應(yīng)用到了DOM上面霹期。
這樣一來(lái)叶组,當(dāng)你在這個(gè)單獨(dú)的虛擬的DOM樹上也一個(gè)接一個(gè)地修改30個(gè)節(jié)點(diǎn)的時(shí)候,它不會(huì)每次都去觸發(fā)重繪历造,所以修改節(jié)點(diǎn)的開銷就變小了甩十。
之后,一旦你要把這些改動(dòng)傳遞給真實(shí)DOM吭产,之前所有的改動(dòng)就會(huì)整合成一次DOM操作侣监。這一次DOM操作引起的布局計(jì)算和重繪可能會(huì)更大,但是相比而言臣淤,整合起來(lái)的改動(dòng)只做一次橄霉,減少了(多次)計(jì)算。不過邑蒋,實(shí)際上不借助Virtual DOM也可以做到這一點(diǎn)姓蜂。你可以自己手動(dòng)地整合所有的DOM操作到一個(gè)DOM 碎片(
DOM fragment
) 里,然后再傳遞給DOM樹医吊。既然如此钱慢,我們?cè)賮?lái)看看Virtual DOM到底解決了什么問題。
首先卿堂,它把管理DOM碎片這件事情自動(dòng)化束莫、抽象化了,使得你無(wú)需再去手動(dòng)處理草描。另外览绿,當(dāng)你要手動(dòng)去做這件事情的時(shí)候,你還得記得哪些部分變化了陶珠,哪些部分沒變挟裂,畢竟之后重繪時(shí),DOM樹上的大量細(xì)節(jié)你都不需要重新刷新揍诽。這時(shí)候Virtual DOM的自動(dòng)化對(duì)你來(lái)說(shuō)就非常有用了诀蓉,如果它的實(shí)現(xiàn)是正確的栗竖,那么它就會(huì)知道到底哪些地方應(yīng)該需要刷新,哪些地方不要渠啤。最后狐肢,Virtual DOM通過各種組件和你寫的一些代碼來(lái)請(qǐng)求對(duì)它進(jìn)行操作,而不是直接對(duì)它本身進(jìn)行操作沥曹,使你不必非要跟Virtual DOM交互份名,也不必非要去了解Virtual DOM修改DOM樹的原理,也就不用再想著去修改DOM了妓美。(譯注:對(duì)開發(fā)者來(lái)說(shuō)僵腺,Virtual DOM幾乎是完全透明的)。這樣你就不用在
修改DOM
和整合DOM操作為一次
之間做同步處理了壶栋。
進(jìn)一步閱讀
以上關(guān)于瀏覽器工作流的內(nèi)容摘錄自這篇文檔中關(guān)于瀏覽器內(nèi)部行為的章節(jié)辰如。這篇文章還深入解釋了瀏覽器引擎的hood部分的一切細(xì)節(jié)。毋庸置疑贵试,這篇文章值得你花時(shí)間從頭到尾好好讀一遍琉兜。它會(huì)幫你很好地理解為什么我們需要Virtual DOM這樣一個(gè)額外的抽象層。
譯注
- [ 1 ] a 3000 feet level view 如果翻譯成 “一個(gè)達(dá)到30000英尺這種級(jí)別的視圖”毙玻,總覺得怪怪的豌蟋,于是翻譯為一個(gè)超大型的視圖。 如果哪位知道信達(dá)雅的翻譯桑滩,請(qǐng)?jiān)u論留言分享一下梧疲,謝謝!
- [ 2 ] Webkit 里使用
layout
表示元素的布局施符,Gecko則稱為Reflow
往声。 - [ 3 ] 遵循慣例,文中一律將
node
(特指DOM樹上的一個(gè)單元)翻譯為節(jié)點(diǎn)戳吝;將element
(特指HTML上的一個(gè)單元)翻譯為元素。 - [ 4 ]
Virtual DOM
即 虛擬 DOM贯涎√蓿考慮到這詞幾乎已經(jīng)稱為一個(gè)專門的術(shù)語(yǔ)了,一般大家也直接用英文稱呼塘雳,不翻譯可能更具可讀性陆盘。