React中一個(gè)沒(méi)人能解釋清楚的問(wèn)題——為什么要使用Virtual DOM

譯者: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的問(wèn)題:

組件化, 單向數(shù)據(jù)綁定,這些我都懂了。但是React為什么要用Virtual DOM呢?

我的回答非常套路,“因?yàn)橹苯硬僮鱀OM比較低效,比較慢。”。

“但是現(xiàn)在的js引擎總是搞個(gè)大新聞,說(shuō)自己的性能比之前又要不知道高到哪里去了。既然如此,為什么還會(huì)說(shuō)直接操作DOM比較慢呢?”

好吧... 這確實(shí)是一個(gè)好問(wèn)題。

驚人的是,我找了半天,發(fā)現(xiàn)并沒(méi)有任何一篇文章可以給出堅(jiān)如磐石的證明,來(lái)完滿(mǎn)地解釋Virtual DOM的必要性。
其實(shí),使得整個(gè)流程變得低效的,并不只有直接操作DOM,還包括了操作DOM之后發(fā)生的事情。

為了能讓你更好地理解Virtual DOM的必要性,我們先來(lái)個(gè)急轉(zhuǎn)彎,從宏觀上來(lái)看瀏覽器的工作流。以及,一次DOM更新后,到底會(huì)發(fā)生什么事呢?

瀏覽器工作流

NOTE:在下面這張圖中,配圖文字使用的是Webkit引擎的術(shù)語(yǔ)。所有的瀏覽器都是遵循類(lèi)似的工作流,僅在細(xì)節(jié)處略有不同。

瀏覽器工作流
瀏覽器工作流

創(chuàng)建DOM樹(shù)

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

創(chuàng)建渲染樹(shù)

  • 同時(shí),瀏覽器也會(huì)解析來(lái)自外部CSS文件和元素上的inline樣式。通常瀏覽器會(huì)為這些樣式信息,連同包含樣式信息的DOM樹(shù)上的節(jié)點(diǎn),再創(chuàng)建另外一個(gè)樹(shù),一般被稱(chēng)作渲染樹(shù)(render tree

創(chuàng)建渲染樹(shù)背后的故事

  • WebKit內(nèi)核的瀏覽器上,處理一個(gè)節(jié)點(diǎn)的樣式的過(guò)程稱(chēng)為attachment。DOM樹(shù)上的每個(gè)節(jié)點(diǎn)都有一個(gè)attach方法,它接收計(jì)算好的樣式信息,返回一個(gè)render對(duì)象(又名renderer
  • Attachment的過(guò)程是同步的,新節(jié)點(diǎn)插入DOM樹(shù)時(shí),會(huì)調(diào)用新節(jié)點(diǎn)的attach方法。
  • 構(gòu)建渲染樹(shù)時(shí),由于包含了這些render對(duì)象,每個(gè)render對(duì)象都需要計(jì)算視覺(jué)屬性(visual properties);這個(gè)過(guò)程通過(guò)計(jì)算每個(gè)元素的樣式屬性來(lái)完成。

布局 Layout

又被簡(jiǎn)稱(chēng)為Reflow[2]

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

繪制 Painting

  • 接著,瀏覽器將會(huì)通過(guò)遍歷渲染樹(shù),調(diào)用每個(gè)節(jié)點(diǎn)的paint方法來(lái)繪制這些render對(duì)象。paint方法根據(jù)瀏覽器平臺(tái),使用不同的UI后端API(agnostic UI backend API)。
    通過(guò)繪制,最終將在屏幕上展示內(nèi)容。

再來(lái)看Virtual DOM

好啦,現(xiàn)在你已經(jīng)簡(jiǎn)單過(guò)了一遍瀏覽器引擎的渲染流程,你可以看到,從創(chuàng)建渲染樹(shù),到布局,一直到繪制,只要你在這過(guò)程中進(jìn)行一次DOM更新,整個(gè)渲染流程都會(huì)重做一遍。尤其是創(chuàng)建渲染樹(shù),它需要重新計(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 操作 真正的問(wèn)題在于每次操作都會(huì)觸發(fā)布局的改變、DOM樹(shù)的修改和渲染。所以,當(dāng)你一個(gè)接一個(gè)地去修改30個(gè)節(jié)點(diǎn)的時(shí)候,就會(huì)引起30次(潛在的)布局重算,30次(潛在的)重繪,等等。

Virtual DOM 實(shí)際上沒(méi)有使用什么全新的技術(shù),僅僅是把 “ 雙緩沖(double buffering)” 技術(shù)應(yīng)用到了DOM上面。
這樣一來(lái),當(dāng)你在這個(gè)單獨(dú)的虛擬的DOM樹(shù)上也一個(gè)接一個(gè)地修改30個(gè)節(jié)點(diǎn)的時(shí)候,它不會(huì)每次都去觸發(fā)重繪,所以修改節(jié)點(diǎn)的開(kāi)銷(xiāo)就變小了。
之后,一旦你要把這些改動(dòng)傳遞給真實(shí)DOM,之前所有的改動(dòng)就會(huì)整合成一次DOM操作。這一次DOM操作引起的布局計(jì)算和重繪可能會(huì)更大,但是相比而言,整合起來(lái)的改動(dòng)只做一次,減少了(多次)計(jì)算。

不過(guò),實(shí)際上不借助Virtual DOM也可以做到這一點(diǎn)。你可以自己手動(dòng)地整合所有的DOM操作到一個(gè)DOM 碎片(DOM fragment) 里,然后再傳遞給DOM樹(shù)。

既然如此,我們?cè)賮?lái)看看Virtual DOM到底解決了什么問(wèn)題。
首先,它把管理DOM碎片這件事情自動(dòng)化、抽象化了,使得你無(wú)需再去手動(dòng)處理。另外,當(dāng)你要手動(dòng)去做這件事情的時(shí)候,你還得記得哪些部分變化了,哪些部分沒(méi)變,畢竟之后重繪時(shí),DOM樹(shù)上的大量細(xì)節(jié)你都不需要重新刷新。這時(shí)候Virtual DOM的自動(dòng)化對(duì)你來(lái)說(shuō)就非常有用了,如果它的實(shí)現(xiàn)是正確的,那么它就會(huì)知道到底哪些地方應(yīng)該需要刷新,哪些地方不要。

最后,Virtual DOM通過(guò)各種組件和你寫(xiě)的一些代碼來(lái)請(qǐng)求對(duì)它進(jìn)行操作,而不是直接對(duì)它本身進(jìn)行操作,使你不必非要跟Virtual DOM交互,也不必非要去了解Virtual DOM修改DOM樹(shù)的原理,也就不用再想著去修改DOM了。(譯注:對(duì)開(kāi)發(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í)別的視圖”,總覺(jué)得怪怪的,于是翻譯為一個(gè)超大型的視圖。 如果哪位知道信達(dá)雅的翻譯,請(qǐng)?jiān)u論留言分享一下,謝謝!
  • [ 2 ] Webkit 里使用layout表示元素的布局,Gecko則稱(chēng)為Reflow。
  • [ 3 ] 遵循慣例,文中一律將node(特指DOM樹(shù)上的一個(gè)單元)翻譯為節(jié)點(diǎn);將element(特指HTML上的一個(gè)單元)翻譯為元素。
  • [ 4 ] Virtual DOM虛擬 DOM??紤]到這詞幾乎已經(jīng)稱(chēng)為一個(gè)專(zhuān)門(mén)的術(shù)語(yǔ)了,一般大家也直接用英文稱(chēng)呼,不翻譯可能更具可讀性。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容