<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Virtual DOM 認(rèn)知誤區(qū)

          共 8283字,需瀏覽 17分鐘

           ·

          2021-02-26 12:55


          作者:莫得鹽 

          鏈接:https://juejin.cn/post/6898526276529684493

          在當(dāng)下最流行的兩個(gè)前端框架都存在 Virtual DOM 的前提下, 漸漸比較多的聽到類似“使用 Virtual DOM 有什么優(yōu)勢?” 的面試題,但一直沒有太在意。直到今天在寫一個(gè)文檔時(shí),突讓想到要把“為什么需要 Virtual DOM ?”也寫進(jìn)去,待我流暢的寫好答案,略一思索——漏洞百出!也不知道是接納了哪方的知識(shí),讓我一直有能輕松回答這個(gè)問題的錯(cuò)覺, 其實(shí)對于這個(gè)問題我是缺乏思考的。

          你或許還不清楚我想說什么,但請耐下心來,先來看看網(wǎng)絡(luò)上關(guān)于此問題的一些見解:

          1. 虛擬DOM同樣也是操作DOM,為啥說它快?-- Segmentfault[1]

            1. 虛擬DOM不會(huì)進(jìn)行排版與重繪操作
            2. 虛擬DOM進(jìn)行頻繁修改,然后一次性比較并修改真實(shí)DOM中需要改的部分(注意!),最后并在真實(shí)DOM中進(jìn)行排版與重繪,減少過多DOM節(jié)點(diǎn)排版與重繪損耗
            3. 真實(shí)DOM頻繁排版與重繪的效率是相當(dāng)?shù)偷?/section>
            4. 虛擬DOM有效降低大面積(真實(shí)DOM節(jié)點(diǎn))的重繪與排版,因?yàn)樽罱K與真實(shí)DOM比較差異,可以只渲染局部(同2)
          2. Virtual Dom 的優(yōu)勢在哪里?-- Github[2]

            1. 具備跨平臺(tái)的優(yōu)勢,由于 Virtual DOM 是以 JavaScript 對象為基礎(chǔ)而不依賴真實(shí)平臺(tái)環(huán)境,所以使它具有了跨平臺(tái)的能力,比如說瀏覽器平臺(tái)、Weex、Node 等。
            2. 操作 DOM 慢,js運(yùn)行效率高。我們可以將DOM對比操作放在JS層,提高效率。因?yàn)镈OM操作的執(zhí)行速度遠(yuǎn)不如Javascript的運(yùn)算速度快,因此,把大量的DOM操作搬運(yùn)到Javascript中,運(yùn)用patching算法來計(jì)算出真正需要更新的節(jié)點(diǎn),最大限度地減少DOM操作,從而顯著提高性能。
            3. 提升渲染性能 Virtual DOM的優(yōu)勢不在于單次的操作,而是在大量、頻繁的數(shù)據(jù)更新下,能夠?qū)σ晥D進(jìn)行合理、高效的更新。
          3. Virtual Dom的優(yōu)勢 -- 掘金[3]

            1. 不會(huì)立即進(jìn)行排版與重繪;
            2. VDOM頻繁修改,一次性比較并修改真實(shí)DOM中需要修改的部分,最后在真實(shí)DOM中進(jìn)行重排 重繪,減少過多DOM節(jié)點(diǎn)重排重繪的性能消耗;
            3. VDOM有效降低大面積真實(shí)DOM的重繪與重排,與真實(shí)DOM比較差異,進(jìn)行局部渲染;

          上面是從 Google 搜索到的三個(gè)平臺(tái)中的分析摘選,總結(jié)下來大概四點(diǎn):

          1. 操作 DOM 太慢,操作 Virtual DOM 對象快
          2. 使用 Virtual DOM 可以避免頻繁操作 DOM ,能有效減少回流和重繪次數(shù)(如果有的話)
          3. 有 diff 算法,可以減少?zèng)]必要的 DOM 操作
          4. 跨平臺(tái)優(yōu)勢,只要有 JS 引擎就能運(yùn)行在任何地方(Weex/SSR)

          它們的理解正確嗎?

          本文測試數(shù)據(jù)都基于 Chrome 86.0.4240.198

          Virtual DOM 快?

          有人認(rèn)為操作 Virtual DOM 速度很快?Virtual DOM 是一個(gè)用來描述 DOM(注意,并不一定一一對應(yīng))的 Javascript 對象,Javascript 操作 Javascript 對象自然是快的。

          但 Virtual DOM 仍然需要調(diào)用 DOM API 去生成真實(shí)的 DOM ,而你其實(shí)是可以直接調(diào)用它們的,所有就有一個(gè)很有意思結(jié)論,正數(shù)再小也不可能比零還小——Virtual DOM 很快,但這并不是它的優(yōu)勢,因你本可以選擇不使用 Virtual DOM 。除了速度不是優(yōu)勢,Virtual DOM 還有個(gè)最大的問題——額外的內(nèi)存占用,以 Vue 的 Virtual DOM 對象為例,100W 個(gè)空的 Virtual DOM(Vue) 會(huì)占用 110M 內(nèi)存。

          內(nèi)存占用截圖:

          測試代碼:

          let creatVNode = function(type{
            return {
              __v_isVNodetrue,
              SKIPtrue,
              type,
              propsnull,
              keynull,
              refnull,
              scopeId0,
              childrennull,
              componentnull,
              suspensenull,
              ssContentnull,
              ssFallbacknull,
              dirsnull,
              transitionnull,
              elnull,
              anchornull,
              targetnull,
              targetAnchornull,
              staticCount0,
              shapeFlag0,
              patchFlag0,
              dynamicPropsnull,
              dynamicChildrennull,
              appContextnull
            }
          }

          let counts = 1000000
          let list = []
          let start = performance.now()
          // 創(chuàng)建 VNode(Vue)
          // 10000: 1120k
          for (let i = 0; i < counts; i++) {
            list.push(creatVNode('div'))
          }

          // 創(chuàng)建 DOM
          // 10000: 320k
          // for (let i = 0; i < counts; i++) {
          //   list.push(document.createElement('div'))
          // }

          console.log(performance.now() - start)

          令人意外的是 100W 個(gè)空的 DOM 對象只占用 45M 內(nèi)存,不清楚在 DOM 屬性明顯更多的情況下 Chrome 是如何優(yōu)化的,或則是 Dev Tools 存在問題,希望有人能替我解惑。

          你看 Virtual DOM 不但執(zhí)行快沒有用,還增加了大量的內(nèi)存消耗,所以我們說它快自然是有問題的,因?yàn)闆]有 Virtual DOM 時(shí)更快。

          Virtual DOM 減少回流和重繪?

          也有人認(rèn)為 Virtual DOM 能減少頁面的 relayout 和 repaint ?通常有兩個(gè)原因來支撐這個(gè)觀點(diǎn):

          1. DOM 操作會(huì)先改變 Virtual DOM ,所以一些無效該變(比如把文本 A 修改為 B ,然后再修改為 A)就不會(huì)調(diào)用 DOM API ,也就不會(huì)導(dǎo)致瀏覽做無效的回流和重繪。
          2. DOM 操作會(huì)先改變 Virtual DOM ,最終由 Virtual DOM 調(diào)用 patch 方法批量操作 DOM ,批量操作就不會(huì)導(dǎo)致過程中出現(xiàn)無意義的回流和重繪。

          無效回流與重繪

          第一個(gè)觀點(diǎn)看著很有道理,但有個(gè)問題很難解釋:瀏覽器的 UI 線程在什么時(shí)候去執(zhí)行回流和重繪?要知道現(xiàn)代瀏覽器在設(shè)計(jì)上為了避免高復(fù)雜度,Javascript 線程和 UI 線程是互斥的,即如果瀏覽器要在 Javascript 執(zhí)行期間觸發(fā) relayout/repaint 則必須先掛起 Javascript 線程,這是個(gè)連我都覺得蠢的設(shè)計(jì),顯然不會(huì)出現(xiàn)在各大瀏覽器身上。

          事實(shí)上也確實(shí)如此,無論你在一次事件循環(huán)中調(diào)用多少次的 DOM API ,瀏覽器也只會(huì)觸發(fā)一次回流與重繪(如果需要),并且如果多次調(diào)用并沒有修改 DOM 狀態(tài),那么回流與重繪一次都不會(huì)發(fā)生

          Timeline 截圖(沒有回流和重繪發(fā)生):

          測試代碼:

          <body>
            <div class="app"></div>
            <scriptlet counts = 1000
              let $app = document.querySelector('.app')
              setTimeout(() => {
                for (let i = 0; i < counts; i++) {
                  $app.innerHTML = 'aaaa'
                  $app.style = 'margin-top: 100px'
                  $app.innerHTML = ''
                  $app.style = ''
                }
              }, 1000) </script>

          </body>

          無意義的回流與重繪

          第二個(gè)觀點(diǎn)是比較有意思的,雖然看了上面的分析,你應(yīng)該也知道它是錯(cuò)的,批量操作并不能減少回流與重繪,因?yàn)樗鼈儽旧砭椭粫?huì)觸發(fā)一次。但我還是要列出來證明一下,因?yàn)檫@是我們當(dāng)下眾多前端的一個(gè)固有思維,我在準(zhǔn)備寫這篇文章前問了一下眾神交流群的朋友們,他們幾乎都掉進(jìn)了這個(gè)認(rèn)知陷阱中,認(rèn)為批量操作會(huì)減少回流與重繪。

          批量操作并不能減少回流與重繪,原因也和上文一致,Javascript 是單線程且與 UI 線程互斥,所以直接放測試數(shù)據(jù):

          Javascript 執(zhí)行耗時(shí)(數(shù)據(jù)取3次平均值):

          Layout 耗時(shí)(數(shù)據(jù)取3次平均值):

          測試代碼:

          <body>
            <div class="app"></div>
            <scriptlet counts = 1000
              let $app = document.querySelector('.app')
              let start = performance.now()
              // 單獨(dú)操作
              // for (let i = 0; i < counts; i++) {
              //   let node = document.createTextNode(`${i}, `)
              //   $app.append(node)
              // }

              // 批量操作
              let $tempContainer = document.createElement('div')
              for (let i = 0; i < counts; i++) {
                let node = document.createTextNode('node,')
                $tempContainer.append(node)
              }
              $app.append($tempContainer)

              console.log(performance.now() - start) </script>

          </body>

          可以看到的是,批量處理和單次處理再 Layout 期間耗時(shí)是幾乎一致的,雖然在 script 執(zhí)行階段還是存在一定的性能優(yōu)勢(大概 30%),但大抵上只要你用好 DOM 操作,批量或不批量帶來的性能影響是很小的( 10W 次調(diào)用多損耗 27ms )。

          題外話:這里提出一個(gè)問題,為什么在 script 執(zhí)行階段還是存在一定的性能差距?答案會(huì)在晚些時(shí)候公布(等我看完這部分邏輯)

          Virtual DOM 有 diff 算法?

          嚴(yán)格來說 diff 算法和 Virtual DOM 是兩個(gè)獨(dú)立的東西,二者互相之間也沒有充分必要的關(guān)聯(lián),比如 svelte[4] 沒有 Virtual DOM 也有其自己的 diff 算法。

          但由于前端框架存在 Virtual DOM 就總有 diff 算法,并且使用了 Virtual DOM 對 diff 算法也有兩個(gè)助力:

          1. 得益于 Virtual DOM 的抽象能力,diff 算法更容易被實(shí)現(xiàn)和理解
          2. 得益于 Virtual DOM Tree 總是在內(nèi)存中, diff 算法功能可以更強(qiáng)大(比如組件移動(dòng),沒有完整的 Tree 結(jié)構(gòu)是不可能實(shí)現(xiàn)的)

          diff 算法能減少 DOM API 調(diào)用,顯然是存在設(shè)計(jì)和性能優(yōu)勢的,而由于 Virtual DOM 的存在,diff 算法可以更方便且更強(qiáng)大,所以我認(rèn)同這是 Virtual DOM 的優(yōu)勢,但不能用“Virtual DOM 有 diff 算法”這樣的表述。

          Virtual DOM 有跨平臺(tái)優(yōu)勢?

          上文提到的 svelte 沒有 Virtual DOM ,但一樣可以實(shí)現(xiàn)服務(wù)端渲染,這說明跨平臺(tái)并不依賴于 Virtual DOM 。

          其實(shí)只要 Javascript 框架有實(shí)現(xiàn)平臺(tái) API 分發(fā)機(jī)制,就能在不同平臺(tái)執(zhí)行不同的渲染方法,即擁有跨平臺(tái)能力。這個(gè)能力的根本,是 Javascript 代碼能低代價(jià)地在各個(gè)平臺(tái)運(yùn)行(得利于瀏覽器在各個(gè)平臺(tái)的普及和 NodeJS),也就是常說的 Javascript 的優(yōu)勢之一是跨平臺(tái)。所以把跨平臺(tái)當(dāng)做 Virtual DOM 的優(yōu)勢,其實(shí)是不正確的,但我們或許應(yīng)該去思考下他們?yōu)槭裁磿?huì)這么認(rèn)為。

          我的想法,可能是這兩個(gè)原因:

          1. Virtual DOM 的優(yōu)勢,可以在不接觸真實(shí) DOM 的情況下操作 DOM,并且性能更好

            在 Virutal DOM 上的改動(dòng),最終還是會(huì)調(diào)用平臺(tái) API 去操作真實(shí)的 DOM ,所以沒有 Virtual DOM 只是相當(dāng)于少了一個(gè)中間抽象層,并不影響跨平臺(tái)能力有無。但還是需要明白,就目前的分析來看,這個(gè)抽象層對跨平臺(tái)能力還是提供了相當(dāng)大的方便(或者說助力)的。

          2. Virtual DOM 在 Vue 中很重要,Vue 本身就是一個(gè)圍繞 Virtual DOM 創(chuàng)建起來的框架,脫離了 Virtual DOM 其設(shè)計(jì)思想必然會(huì)和當(dāng)下迥乎不同

          總結(jié)

          本文從互聯(lián)網(wǎng)上摘選了部分對開發(fā)者對 Virtual DOM 優(yōu)點(diǎn)的認(rèn)知,也從現(xiàn)實(shí)生活中了解到一些誤解,總結(jié)為 Virtual DOM 的四個(gè)“優(yōu)勢”,并分別對這四個(gè)“優(yōu)勢”進(jìn)行了單獨(dú)分析或舉證。

          最終我們識(shí)別了幾個(gè)關(guān)于 Virtual DOM 優(yōu)勢誤區(qū):

          1. 操作 DOM 太慢,操作 Virtual DOM 對象快 ?

            Virtual DOM 很快,但這并不是它的優(yōu)勢,因你本可以選擇不使用 Virtual DOM 。

          2. 使用 Virtual DOM 可以避免頻繁操作 DOM ,能有效減少回流和重繪次數(shù) ?

            無論你在一次事件循環(huán)中調(diào)用多少次的 DOM API ,瀏覽器也只會(huì)觸發(fā)一次回流與重繪(如果需要),并且如果多次調(diào)用并沒有修改 DOM 狀態(tài),那么回流與重繪一次都不會(huì)發(fā)生。批量操作也不能減少回流與重繪。

          3. Virtual DOM 有跨平臺(tái)優(yōu)勢 ?

            跨平臺(tái)是 Javascript 的優(yōu)勢,與 Virtual DOM 無關(guān)。

          我們也提到了 Virtual DOM 真正的優(yōu)點(diǎn)是其抽象能力和常駐內(nèi)存的特性,讓框架能更容易實(shí)現(xiàn)更強(qiáng)大的 diff 算法,缺點(diǎn)是增加了框架復(fù)雜度,也占用了更多的內(nèi)存

          參考資料

          [1]

          虛擬DOM同樣也是操作DOM,為啥說它快?-- Segmentfault:https://segmentfault.com/q/1010000010303981

          [2]

          Virtual Dom 的優(yōu)勢在哪里?-- Github:https://github.com/RomanHc/blog/issues/20

          [3]

          Virtual Dom的優(yōu)勢 -- 掘金:https://juejin.cn/post/6844904179715014669

          [4]

          svelte:https://github.com/sveltejs/svelte

          瀏覽 43
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久亚洲AV综合波多野结衣 | 婷婷五月天婷婷五月天婷婷五月天色 | 免费crm一区二区 | 操B1网| 免费欧美成人网站 |