關(guān)于 Virtual DOM 和直接 DOM 操作哪個(gè)性能更高?

網(wǎng)上關(guān)于 Virtual DOM(下稱VDOM) 和 直接操作 DOM 來更新頁面誰的性能更高有許多爭論。這里就 VDOM 的性能問題簡單談下自己的理解。

一、什么是 VDOM

VDOM 本質(zhì)上是一個(gè) Javascript 對象,用來描述 DOM 結(jié)構(gòu),如:

<html>
    <head></head>
    <body>
        <ul class="list-ul">
            <li class="list-one">List One</li>
        </ul>
    </body>
</html>

可以用如下對象表示:

const vdom = {
    tagName: "html",
    children: [
        { tagName: "head" },
        {
            tagName: "body",
            children: [
                {
                    tagName: "ul",
                    attributes: { "class": "list-ul" },
                    children: [
                        {
                            tagName: "li",
                            attributes: { "class": "list-one" },
                            textContent: "List One"
                        }
                    ]
                }
            ]
        }
    ]
}

在實(shí)際的生產(chǎn)環(huán)境需要將這個(gè) JS 對象轉(zhuǎn)化成真實(shí)的 DOM 元素。

二、Js 操作 DOM 的開銷

1、瀏覽器渲染引擎工作流程大致分為如下步驟:

  • 解析 HTML 創(chuàng)建 DOM 樹
    當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)都構(gòu)建好后才會去構(gòu)建當(dāng)前節(jié)點(diǎn)的下一個(gè)兄弟節(jié)點(diǎn)。
  • 解析 CSS 創(chuàng)建 CSS 規(guī)則樹
  • 合并 DOM 樹和 CSS規(guī)則,生成 render 樹
  • Layout / Reflow:布局 render 樹,計(jì)算各個(gè)元素的尺寸和位置等
  • Paint:繪制頁面內(nèi)容

2、JS 頻繁操 DOM 的開銷:

  • 在上述渲染過程中,前3點(diǎn)可能要多次執(zhí)行,比如 Js 操作 DOM、更改 CSS 樣式時(shí),瀏覽器又要重新構(gòu)建 DOM、CSS 規(guī)則樹,重新 render,重新 Layout、Paint;
  • Layout 在 Paint 之前,因此每次 Layout 重新布局(Reflow 回流)后都要重新 Paint / 渲染,這時(shí)又要去消耗 GPU 資源
  • Paint 不一定會觸發(fā) Layout,比如顏色、背景的改變,只需要 Repaint / 重繪
  • 圖片下載完也會重新出發(fā) Layout 和 Paint
  • 雖然瀏覽器針對渲染流程有優(yōu)化,但是這個(gè)過程開銷還是巨大的。

三、為什么需要 VDOM

假如在實(shí)際生產(chǎn)環(huán)境中,有這么一個(gè)列表:

<ul>
    <li><span>item 1</span></li>
    <li><span>item 2</span></li>
    <li><span>item 3</span></li>
    ...
    <li><span>item 100</span></li>
</ul>

我們現(xiàn)在需要更新列表的,從后端拿到了數(shù)據(jù),但是新的數(shù)據(jù)只有第50行的數(shù)據(jù)有變化。
1、最簡單最節(jié)約心智的辦法是,我們不關(guān)心新數(shù)據(jù)與老數(shù)據(jù)之間的差異,直接 innerHTML 更新整個(gè) ul 標(biāo)簽里的內(nèi)容。但是這樣就造成了不必要的 DOM 操作開銷,畢竟只需要更新一個(gè)節(jié)點(diǎn),但是為了省事,99次操作是浪費(fèi)資源的無用功。
2、或者我們逐個(gè)對比數(shù)據(jù),發(fā)現(xiàn)是第50行有變化,直接將第50行的 span 用 innerText 更新內(nèi)容即可。
3、雖然手動更新第50行內(nèi)容達(dá)到了最小操作,但是每次從后端拿到新數(shù)據(jù),我們并不能都知道是哪些行數(shù)據(jù)有變化,這個(gè)時(shí)候就需要寫一個(gè)通用的方法來比較新舊數(shù)據(jù)的變化,并只去更新數(shù)據(jù)有變化的節(jié)點(diǎn)。
但是這樣還不夠,這個(gè)通用方法也只是滿足了這一個(gè)列表的數(shù)據(jù)對比和節(jié)點(diǎn)更新,如果我們的項(xiàng)目中還有幾十、幾百個(gè)其它的列表呢?
這個(gè)時(shí)候 VDOM 就派上用場了。我們把整個(gè)頁面抽象成 Js 對象(VDOM),每次的更新數(shù)據(jù),我們都先更新 VDOM,再通過比較 新舊 VDOM 的變化,找到具體要更新的節(jié)點(diǎn),再去操作具體的 DOM。
這個(gè)時(shí)候可能有人要問了,那在更新 VDOM 的時(shí)候不也做了很多無用功的操作嗎?
對,但是 VDOM 的操作都是純 Js 的計(jì)算,大家要明確的一點(diǎn)是 Js 計(jì)算(特別是在 V8 引擎的加持下)要比真實(shí) DOM 操作開銷小得多,最重要的是再也不用操心到底哪些數(shù)據(jù)有更新了,直接無腦用 VDOM 就是,開發(fā)效率提高了!

四、那到底誰的效率更高

用這個(gè)列表舉例:

<ul>
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
    ...
    <li>item 100</li>
</ul>

1、當(dāng)列表新數(shù)據(jù)更新了第50、60行時(shí):

  • 手動對第50、60行的 span 節(jié)點(diǎn)進(jìn)行 innerText操作,這是性能最高的,我們計(jì)它的耗時(shí)為 T_1。
  • 直接 innerHTML 整個(gè) ul 列表,不考慮瀏覽器渲染優(yōu)化等情況,簡單的認(rèn)為會多出88次節(jié)點(diǎn)操作,計(jì)為 T_2。
  • 通過 VDOM 來更新 DOM :
    抽象 DOM 結(jié)構(gòu)為 VDOM -> Diff 策略比較變化 -> 更新真實(shí) DOM
    則耗時(shí)為 Js 計(jì)算 VDOM 變化(T_3)與最小節(jié)點(diǎn)操作(T_1)之和,計(jì)為: T_1 + T_3。

前面也說了得益于現(xiàn)代瀏覽器的高效,Js 計(jì)算是非常快的,故 T_1 + T_3 << T_2。
綜合比較三種更新方式,T_1 < T_1 + T_3 << T_2。
可以看出手動去進(jìn)行最小節(jié)點(diǎn)操作是性能最好的,但是其心智負(fù)擔(dān)也不小。

2、當(dāng) ul 中所有數(shù)據(jù)都變了,那就能直接無腦 innerHTML 進(jìn)行更新,因?yàn)榇藭r(shí)更新所有節(jié)點(diǎn)就是最小節(jié)點(diǎn)操作。
所以一個(gè)項(xiàng)目能確定基本上每一頁的內(nèi)容都不相同,幾乎要全部更新,那可以不用 VDOM,直接用 innerHTML 即可。
反之一個(gè)項(xiàng)目,有很多列表,有眾多增刪改查操作,那用 VDOM 是十分有意義的。

所以拋開場景談性能就如同拋開劑量談毒性,都是耍流氓。用 VDOM 更新真實(shí) DOM 其實(shí)是在節(jié)約開銷(運(yùn)行效率)和節(jié)約心智(省事、提高開發(fā)效率)之間找到一個(gè)比較好的平衡點(diǎn)。不然為什么人家 React 和 Vue 要用 VDOM 呢?成千上萬人驗(yàn)證過的東西,能流行一定有它的道理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 一、原理: 我們知道DOM是一個(gè)js對象,操作這個(gè)對象后,會觸發(fā)一些瀏覽器行為,比如 layout(布局),pai...
    鐘鐘353251閱讀 850評論 0 0
  • 翻譯自:高性能Javascript 第三章Dom操作是昂貴的,它通常是web應(yīng)用的性能瓶頸。這篇文章討論Dom操作...
    Addy_Zhou閱讀 3,159評論 0 5
  • 參考文章:深度剖析:如何實(shí)現(xiàn)一個(gè)Virtual DOM 算法 作者:戴嘉華React中一個(gè)沒人能解釋清楚的問題——...
    waka閱讀 6,152評論 0 21
  • 1、真實(shí)DOM的解析流程 瀏覽器渲染引擎工作流程都差不多,大致分為5步 創(chuàng)建DOM樹: 創(chuàng)建StyleRules ...
    懂會悟閱讀 430評論 0 1
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,879評論 28 54

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