1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        深度對(duì)比 React 與 Vue 的 Diff 算法

        共 11938字,需瀏覽 24分鐘

         ·

        2024-08-05 22:39

        昨天接了個(gè)廣子,恰了點(diǎn)飯。你們的吐槽我的認(rèn)了,哎呀,你們都說(shuō)得對(duì)。所以我今晚連夜爆肝寫了一篇專門用來(lái)面試的干貨,總計(jì) 7000 字左右,分享給大家,必須要搶救一下想要取關(guān)的大佬們!

        許多朋友會(huì)在簡(jiǎn)歷上同時(shí)寫自己會(huì) React、Vue,但是倒霉的面試官一看到這種簡(jiǎn)歷,就喜歡問(wèn)它們有什么區(qū)別。其中頻率比較高的一個(gè)問(wèn)題就是 React 與 Vue 的 diff 算法有啥相同之處和不同之處...

        很顯然,這種問(wèn)題對(duì)于面試考驗(yàn)開(kāi)發(fā)者能力而言,沒(méi)啥營(yíng)養(yǎng),就算知道了,對(duì)開(kāi)發(fā)能力也不會(huì)有什么明顯的提高,還不如更具體的問(wèn) key 值有什么用呢,但是沒(méi)辦法,有的面試官就是愛(ài)問(wèn),既然這樣,那我們就答給他們看。


        假說(shuō)論

        我們?cè)谒伎妓惴▎?wèn)題的時(shí)候,一定要謹(jǐn)記一個(gè)前提,那就是沒(méi)有完美的算法可以解決所有問(wèn)題。 因此,在設(shè)計(jì)一個(gè)算法時(shí),我們需要充分考慮應(yīng)用場(chǎng)景,然后提出一個(gè)假說(shuō),從而極大的減少問(wèn)題的復(fù)雜性,讓解決方案變得更加簡(jiǎn)單。

        在 React/Vue 的 diff 算法中,當(dāng)我們要對(duì)比前后兩棵樹(shù)的差異時(shí),我們的目標(biāo)是盡可能少的創(chuàng)建節(jié)點(diǎn)。但是由于 DOM 操作的可能性太復(fù)雜了,因此如果要全部對(duì)比出來(lái),復(fù)雜度就非常高。達(dá)到了 O(n^ 3) 這個(gè)級(jí)別。

        之所以這么復(fù)雜的原因,就是因?yàn)楣?jié)點(diǎn)不僅可以增加刪除,還可以移動(dòng)。我們要分辨節(jié)點(diǎn)是否從子元素移動(dòng)到了父元素,或者增加了一個(gè)父元素,判斷過(guò)程非常復(fù)雜。因此,在設(shè)計(jì) dfiff 算法的時(shí)候,React/Vue 都放棄了這種情況的識(shí)別。

        他們根據(jù)實(shí)際情況提出的假說(shuō)是:在實(shí)際情況中,整棵 DOM 樹(shù)里,關(guān)于父子節(jié)點(diǎn)移動(dòng)的情況是比較少的,因此,沒(méi)有必要為了這種少部分情況加劇算法的壓力。只要放棄識(shí)別這種情況,算法就能夠得到極大的簡(jiǎn)化。


        幾乎相同的比較策略

        在上面我們提到的假說(shuō)論之下,React/Vue 在 diff 算法上,都使用了非常類似的策略。

        一、同層比較

        如下圖所示,diff 算法只需要比較相同層級(jí)的節(jié)點(diǎn)是否相同

        比較之后又兩種情況

        • 如果相同:則直接復(fù)用,而不需要重新創(chuàng)建
        • 如果不同:則直接默認(rèn)為從該節(jié)點(diǎn)開(kāi)始,以下的全部節(jié)點(diǎn)都發(fā)生了變化,需要重新創(chuàng)建。

        如下圖所示,雖然節(jié)點(diǎn)只是發(fā)生了移動(dòng),但是在 diff 過(guò)程中,會(huì)被認(rèn)為 A 節(jié)點(diǎn)已經(jīng)被刪除,然后重新創(chuàng)建它。

        因此,在開(kāi)發(fā)過(guò)程中,我們可以通過(guò)避免跨層級(jí)操作節(jié)點(diǎn)的方式,來(lái)提高你應(yīng)用程序的性能。

        二、節(jié)點(diǎn)比較

        在對(duì)比節(jié)點(diǎn)是否相同時(shí),React 與 Vue 的處理策略也比較類似。了解節(jié)點(diǎn)的比較方式,是我們?cè)谧鲂阅軆?yōu)化時(shí)的重要依據(jù)。

        節(jié)點(diǎn)比較有兩種情況,一種節(jié)點(diǎn)是默認(rèn)支持的 DOM 節(jié)點(diǎn),一種是通過(guò)自定義創(chuàng)造出來(lái)的自定義節(jié)點(diǎn),在 React 中會(huì)區(qū)分這兩種情況,但是在 Vue 中會(huì)統(tǒng)一只識(shí)別標(biāo)簽名 tag。

        除了這個(gè)情況之外,在結(jié)構(gòu)類型上,還分為兩種情況,一種是正常的樹(shù)狀結(jié)構(gòu),另外一種是列表結(jié)構(gòu)。列表結(jié)構(gòu)通常情況下我們就不能直接忽視移動(dòng)這種情況。因此,針對(duì)于這種列表結(jié)構(gòu),React 和 Vue 都優(yōu)先提倡使用傳入 key 值的方式進(jìn)行標(biāo)記,從而極大的減小比較壓力。

        因此,在列表節(jié)點(diǎn)的比較中,React、Vue 都優(yōu)先比較 key 值。

        !

        不過(guò)針對(duì)列表的比較方式處理,是 React 和 Vue 在 diff 算法上最大的差別。我們后面單獨(dú)介紹

        然后,在 React 中,diff 算法會(huì)優(yōu)先比較節(jié)點(diǎn)類型是否相同。例如下面的代碼,divspan 屬于不同的節(jié)點(diǎn)類型,那么就表示不是同一個(gè)節(jié)點(diǎn)。

        <div>
          <Counter />
        </div>

        <span>
          <Counter />
        </span>

        然后會(huì)比較 props、state、context 等外部傳入的參數(shù)是否相同,從而判斷是否是同一個(gè)節(jié)點(diǎn)。當(dāng)然,這里還有一個(gè)重要的細(xì)節(jié),對(duì)于性能優(yōu)化至關(guān)重要。

        由于在 React 中,props 的傳入都是通過(guò)類似于函數(shù)傳參的方式傳入,例如

        function Counter({ a: 1 }{...}

        前后兩次執(zhí)行

        Counter({a1})
        Counter({a1})

        這里的細(xì)節(jié)就是,雖然兩次都入?yún)⒍际?{a: 1},但是他們是不同的內(nèi)存地址,因此前后兩次 props 的比較結(jié)果始終為 false

        {a1} === {a1// false

        如果不解決這個(gè)問(wèn)題,任何比較方式都是沒(méi)有意義的,結(jié)果都是為 false。

        所里這里 React 設(shè)計(jì)了一個(gè)巧妙的規(guī)則,那就是當(dāng)我們判定元素節(jié)點(diǎn)的父節(jié)點(diǎn)未發(fā)生變化時(shí),就不比較 props,從而跳過(guò) props 的比較,來(lái)提高性能。我們可以利用這樣的特性在 React 中寫出性能非常高的代碼。

        除此之外,React 還設(shè)計(jì)了一個(gè) api,React.memo,該 api 可以改變 props 的比較規(guī)則。使用方式如下所示,memo 接收組件聲明作為參數(shù)

        function Counter({ a: 1, b: 2 }{...}

        export default React.memo(Counter)

        使用 memo 包裹之后,props 的比較規(guī)則變成了依次比較第一層 key 值所對(duì)應(yīng)的值。例如,上面的案例中,包裹之前的比較方式為

        a1b2 } === { a1b2 } // 始終為 false

        包裹之后的比較方式為

        p1.a === p2.a && p1.b === p2.b // true

        因此,在特定的場(chǎng)景中,使用 memo 可以有效命中可復(fù)用節(jié)點(diǎn)。

        在 Vue 中,由于并不是那么強(qiáng)依賴 diff 算法,因此它的節(jié)點(diǎn)比較邏輯相對(duì)而言會(huì)簡(jiǎn)單直接一些,通過(guò)一個(gè) sameVnode 方法來(lái)比較節(jié)點(diǎn)是否相同。

        // Vue2 /src/core/vdom/patch.js
        // 主要通過(guò)對(duì)key和標(biāo)簽名做比較
        function sameVnode (a, b{
          return (
            a.key === b.key && // 標(biāo)簽名是否一樣
            a.asyncFactory === b.asyncFactory && ( // 是否都是異步工廠方法
              (
                a.tag === b.tag && // 標(biāo)簽名是否一樣
                a.isComment === b.isComment && // 是否都為注釋節(jié)點(diǎn)
                isDef(a.data) === isDef(b.data) && // 是否都定義了data
                sameInputType(a, b)  // 當(dāng)標(biāo)簽為input時(shí),type必須是否相同
              ) || (
                isTrue(a.isAsyncPlaceholder) && // 是否都有異步占位符節(jié)點(diǎn)
                isUndef(b.asyncFactory.error)
              )
            )
          )
        }
        function sameInputType(a, b{
          if (a.tag !== 'input'return true
          let i
          const typeA = isDef((i = a.data)) && isDef((i = i.attrs)) && i.type
          const typeB = isDef((i = b.data)) && isDef((i = i.attrs)) && i.type
          return typeA === typeB || (isTextInputType(typeA) && isTextInputType(typeB))
        }

        這里需要注意的是,如果我們要詳細(xì)的聊清楚 React 和 Vue 在節(jié)點(diǎn)是否相同的比較方式上時(shí),就要明白 React 是強(qiáng)依賴于這個(gè),但是 Vue 依賴較弱,因?yàn)?Vue 的實(shí)現(xiàn)原理中更多的是通過(guò)依賴收集的方式來(lái)找到需要更新的節(jié)點(diǎn),這也導(dǎo)致了 React 在這個(gè)上面的邏輯要更加復(fù)雜,Vue 則更加簡(jiǎn)單。因此,我們需要對(duì)他們各自的原理背景有一定的了解。


        最大的區(qū)別,在列表上的處理

        列表是性能問(wèn)題頻發(fā)的重要場(chǎng)景,因此,React 和 Vue 都針對(duì)長(zhǎng)列表設(shè)計(jì)了特殊的處理方式。在聊之前,我們要明確處理場(chǎng)景,那就是,在列表中,我們就不能再忽視節(jié)點(diǎn)位置的移動(dòng)了。

        因?yàn)?,一個(gè)簡(jiǎn)單的移動(dòng),就很容易會(huì)造成整個(gè)組件被判定為需要全部重新創(chuàng)建。所以,我們需要判斷出來(lái)節(jié)點(diǎn)是只發(fā)生了移動(dòng),而不是需要重新創(chuàng)建。

        給列表中的每一個(gè)節(jié)點(diǎn),引入唯一 key 值,是他們都共同采用的技術(shù)手段。通過(guò)唯一key 值,我們可以在舊列表中找到新列表的節(jié)點(diǎn)是否已經(jīng)存在,從而決定是移動(dòng)節(jié)點(diǎn)的位置還是創(chuàng)建新的節(jié)點(diǎn)。我們通常會(huì)在數(shù)組中設(shè)定一個(gè) id 用于表示唯一 key 值。

        const todoItems = todos.map((todo) =>
          <li key={todo.id}>
            {todo.text}
          </li>

        );

        需要注意的是,這里的唯一 key 值,盡量不要使用遞增的數(shù)字序列來(lái)表示。

        const todoItems = todos.map((todo, index) =>
          // Only do this if items have no stable IDs
          <li key={index}>
            {todo.text}
          </li>

        );

        這個(gè)問(wèn)題也是面試中經(jīng)常會(huì)聊到的話題:為什么盡量不要使用 index 序列作為 key。這是因?yàn)楫?dāng)我們?cè)诹斜碇行略鲆粋€(gè)內(nèi)容時(shí),每一項(xiàng)的 index 每次都會(huì)發(fā)生變化,從而讓渲染結(jié)果出現(xiàn)混亂。

        例如有一個(gè)數(shù)組為  [a, b, c],此時(shí)我們將 index 作為key值,那么數(shù)組項(xiàng)與 key 值的對(duì)應(yīng)關(guān)系就是

        [a:0, b:1, c:2]

        此時(shí),我們往數(shù)組頭部添加一個(gè)數(shù)據(jù),這個(gè)時(shí)候數(shù)組變成了 [p, a, b, c],然后再執(zhí)行,數(shù)組項(xiàng)與 key 的對(duì)應(yīng)關(guān)系就變成了

        [p:0, a:1, b:2, c:3]

        新增的項(xiàng)是 p,但是最終導(dǎo)致了數(shù)組項(xiàng)與key 之間的對(duì)應(yīng)關(guān)系錯(cuò)亂,結(jié)果導(dǎo)致緩存的節(jié)點(diǎn)掛到了錯(cuò)誤的數(shù)組項(xiàng)上去了。我們可以通過(guò)如下案例演示觀察在 UI 上的表現(xiàn)差別

        首先,我們的默認(rèn)情況如下,上面的列表使用 index 作為 key 值,下面的列表使用 唯一 id 作為key 值。

        當(dāng)我新增一個(gè)項(xiàng)時(shí),仔細(xì)觀察兩個(gè)列表的差異。

        在引入了 key 值之后,為了追求在某些情況下更少的移動(dòng)次數(shù),Vue 中使用了更復(fù)雜的算法設(shè)計(jì):雙端比較以及最長(zhǎng)子序列遞增,這是他們最大的區(qū)別。


        React 的比較方式

        首先,我們假定有一個(gè)已經(jīng)渲染好的列表節(jié)點(diǎn)如下

        // 使用 _ 表示舊節(jié)點(diǎn)列表
        [_A] [_B] [_C] [_D]

        然后,我們?cè)?A 的前面新增的了一個(gè)節(jié)點(diǎn),P,理想的結(jié)果如下所示

        [P] [A] [B] [C] [D]

        在比較的過(guò)程中,我們會(huì)首先創(chuàng)建一個(gè)變量 lastIndex,默認(rèn)值為 0。然后使用一個(gè)指針 index 用來(lái)記錄新數(shù)組中的當(dāng)前項(xiàng)在舊列表中的索引值。

        在比較的過(guò)程中,lastIndex 的變化規(guī)則如下

        lastIndex = max(index, lastIndex)

        例如,我們開(kāi)始遍歷新數(shù)據(jù) [P, A, B, C D]

        1、第一個(gè)目標(biāo)項(xiàng)為 P,我們會(huì)去舊列表中查詢是否存在相同的 key=P 的項(xiàng)。發(fā)現(xiàn)沒(méi)有,此時(shí)創(chuàng)建新節(jié)點(diǎn)

        lastIndex = 0

        [P]

        2、第二個(gè)目標(biāo)項(xiàng)為 A,我們?nèi)ヅf列表中查詢是否存在相同的,發(fā)現(xiàn)有,此時(shí) index = 0,可復(fù)用節(jié)點(diǎn)

        當(dāng)滿足條件 index < lastIndex 時(shí),移動(dòng)節(jié)點(diǎn)。此時(shí) index = 0,lastIndex = 0,不滿足條件,不移動(dòng),此時(shí)結(jié)果為

        // 結(jié)果為 0
        lastIndex = max(index, lastIndex)

        [P] [A]
        i

        注意:這里說(shuō)的移動(dòng)節(jié)點(diǎn),指的是對(duì)真實(shí) DOM 的操作。

        ?

        這里需要多花點(diǎn)時(shí)間感受一下這個(gè)判斷條件的合理性, index < lastIndex,他是為了確保索引的正確順序而設(shè)計(jì)的規(guī)則

        3、第三個(gè)目標(biāo)項(xiàng)為 B,我們?nèi)ヅf列表中查詢是否存在相同的key,發(fā)現(xiàn)有,此時(shí) index = 1,可復(fù)用節(jié)點(diǎn)

        不滿足 index(1) < lastIndex(0),不移動(dòng),結(jié)果為

        // 結(jié)果為 1
        lastIndex = max(index, lastIndex)

        [P] [A] [B]

        依次類推,最終我們發(fā)現(xiàn),這種情況下,只需要?jiǎng)?chuàng)建一個(gè)新節(jié)點(diǎn) P,不需要移動(dòng)任何節(jié)點(diǎn)。

        第二個(gè)案例

        新舊列表節(jié)點(diǎn)如下

        舊列表:[A] [B] [C] [D] 
        新列表:[B] [A] [D] [C]

        新的數(shù)據(jù)為 [B, A, D, C]

        1、第一個(gè)目標(biāo)節(jié)點(diǎn)為 B,發(fā)現(xiàn)在舊列表中存在相同 key,那么復(fù)用節(jié)點(diǎn),此時(shí),index = 1,當(dāng)前結(jié)果為

        index = 1
        lastIndex = 0
        index < lastIndex // false,不移動(dòng)

        lastIndex = max(index, lastIndex) // 1

        [B]

        2、第二個(gè)目標(biāo)節(jié)點(diǎn)為 A,發(fā)現(xiàn)在舊列表中存在相同 key,那么復(fù)用節(jié)點(diǎn),此時(shí),index = 0,當(dāng)前結(jié)果為

        index = 0
        lastIndex = 1
        index < lastIndex // true,移動(dòng)

        lastIndex = max(index, lastIndex) // 1

        [B] [A]

        3、第三個(gè)目標(biāo)節(jié)點(diǎn)為 D,發(fā)現(xiàn)在舊列表中存在相同 key,那么復(fù)用節(jié)點(diǎn),此時(shí),index = 3,當(dāng)前結(jié)果為

        index = 3
        lastIndex = 1
        index < lastIndex // false,不移動(dòng)

        lastIndex = max(index, lastIndex) // 3

        [B] [A] [D]

        4、第四個(gè)目標(biāo)節(jié)點(diǎn)為 C,發(fā)現(xiàn)在舊列表中存在相同 key,那么復(fù)用節(jié)點(diǎn),此時(shí),index = 2,當(dāng)前結(jié)果為

        index = 2
        lastIndex = 3
        index < lastIndex // true,移動(dòng)

        lastIndex = max(index, lastIndex) // 3

        [B] [A] [D] [C]

        這個(gè)案例在 diff 之后,只需要真實(shí) DOM 移動(dòng)兩次節(jié)點(diǎn)。就可以完成更新了。

        相信通過(guò)這兩個(gè)案例,大家應(yīng)該能掌握 React 在列表中的對(duì)比規(guī)則,接下來(lái),我們來(lái)了解一下 Vue 的雙端對(duì)比。


        Vue 雙端比較

        Vue 的雙端對(duì)比的設(shè)計(jì)有一個(gè)目標(biāo),那就是在特定的場(chǎng)景之下,減少真實(shí) DOM 的移動(dòng)次數(shù)。我們來(lái)看一下這樣一種場(chǎng)景。

        舊:[A] [B] [C] [D]
        新:[D] [A] [B] [C]

        如果按照 React 的比較規(guī)則,此時(shí)由于第一個(gè)目標(biāo) D 的 index 為 3,從一開(kāi)始就變成了最大,因此,后續(xù)的 lastIndex 都為 3,所有的目標(biāo)項(xiàng)都會(huì)滿足 index < lastIndex,因此,真實(shí) DOM 的移動(dòng)就會(huì)執(zhí)行 3 次。

        而 Vue 提出的雙端比較,目標(biāo)就是希望可以識(shí)別出來(lái)這種情況,只需要讓移動(dòng)只發(fā)生一次即可。就是 D 從最末尾移動(dòng)到最前面。

        雙端比較會(huì)使用 4 個(gè)指針,分別記錄舊列表和新列表的首尾兩個(gè)節(jié)點(diǎn)位置。

        let oldStartIdx = 0
        let oldEndIdx = prevChildren.length - 1
        let newStartIdx = 0
        let newEndIdx = nextChildren.length - 1

        以及對(duì)應(yīng)的虛擬節(jié)點(diǎn)對(duì)象

        let oldStartVNode = prevChildren[oldStartIdx]
        let oldEndVNode = prevChildren[oldEndIdx]
        let newStartVNode = nextChildren[newStartIdx]
        let newEndVNode = nextChildren[newEndIdx]

        比較的規(guī)則遵循:首-首比較,尾-尾比較,尾-首比較,首-尾比較的順序。通過(guò)這樣方式找出首尾是否有節(jié)點(diǎn)可以被復(fù)用。

        while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
          if (oldStartVNode.key === newStartVNode.key) {
            // ...
          } else if (oldEndVNode.key === newEndVNode.key) {
            // ...
          } else if (oldStartVNode.key === newEndVNode.key) {
            // ...
          } else if (oldEndVNode.key === newStartVNode.key) {
            // ...
          }
        }

        例如下面案例

        舊:[A] [B] [C] [D]
        新:[D] [A] [B] [C]

        在首次經(jīng)過(guò)四次比較之后,發(fā)現(xiàn) 新首-舊尾 key 值相同,可以復(fù)用,此時(shí)需要通過(guò)移動(dòng)首尾索引指針構(gòu)建新的新舊節(jié)點(diǎn)數(shù)組

        // 發(fā)現(xiàn)復(fù)用節(jié)點(diǎn),update
        patch(oldEndVNode, newStartVNode, container)
        // 移動(dòng)
        container.insertBefore(oldEndVNode.el, oldStartVNode.el)
        // 更新索引指針
        oldEndVNode = prevChildren[--oldEndIdx]
        newStartVNode = nextChildren[++newStartIdx]

        然后新的新舊數(shù)組為

        舊:[A] [B] [C] -[D]
        新:-[D] [A] [B] [C]

        演變?yōu)?br>
        舊:[A] [B] [C]
        新:[A] [B] [C]

        得到新的新舊數(shù)組,繼續(xù)重復(fù)首-首比較,尾-尾比較,尾-首比較,首-尾比較。直到比較結(jié)束。

        指針的移動(dòng)方向,總體趨勢(shì)是從兩端往中間移動(dòng)。

        這里需要特別注意的是,這樣的比較方式雖然快,但這是不充分比較,因此,在許多情況下,會(huì)導(dǎo)致節(jié)點(diǎn)存在復(fù)用,但是沒(méi)有找出來(lái)。因此,如果沒(méi)找到可復(fù)用的節(jié)點(diǎn),比較還沒(méi)有結(jié)束,還有后續(xù)的邏輯。

        我們構(gòu)建一個(gè)新的案例,如下所示

        舊:[A] [B] [C] [D]
        新:[B] [D] [A] [C]

        此時(shí)我們發(fā)現(xiàn),通過(guò)首位的 4 次比較,結(jié)果一個(gè)可復(fù)用的節(jié)點(diǎn)都沒(méi)找到,因此,此時(shí)需要回過(guò)頭來(lái)重新完整的遍歷,以新首 key 為當(dāng)前目標(biāo),去舊列表中依次查找是否存在可復(fù)用節(jié)點(diǎn)

        在這個(gè)案例中,可以找到,那么,我們就把 B 移動(dòng)到首位

        舊:[A] -[B] [C] [D]
        新:-[B] [D] [A] [C]

        移動(dòng)之后,通過(guò)改變指針位置,再將新的雙端列表演變?yōu)?/p>

        舊:[A] [C] [D]
        新:[D] [A] [C]

        然后再以新尾 key 作為當(dāng)前目標(biāo),去舊列表中依次遍歷找尋??梢哉业?C,將 C 移動(dòng)到尾部。然后依次類推。

        最后,如果雙端對(duì)比結(jié)束之后,舊列表中還存在節(jié)點(diǎn),則表示這些節(jié)點(diǎn)需要被批量刪除

        如果對(duì)比結(jié)束之后,新列表中還存在節(jié)點(diǎn),則表示這些節(jié)點(diǎn)需要批量新增,簡(jiǎn)單處理即可。

        Vue3 中的處理方式又發(fā)生了變化,但是時(shí)間來(lái)不及太詳細(xì)的分析了,我這里就簡(jiǎn)單說(shuō)一下,可以用于面試的術(shù)語(yǔ):

        先處理左側(cè)相同部分,再處理右側(cè)相同部分,鎖定可復(fù)用的節(jié)點(diǎn)之后,再針對(duì)中間亂序部分,采用最長(zhǎng)遞增子序列的算法,計(jì)算出亂序部分可以復(fù)用的最長(zhǎng)連續(xù)節(jié)點(diǎn)。通過(guò)這個(gè)遞增數(shù)組,我們就可以知道哪些節(jié)點(diǎn)在變化前后的相對(duì)位置沒(méi)有發(fā)生變化,哪些需要移動(dòng)。

        ?

        關(guān)于最長(zhǎng)遞增子序列大家可以通過(guò)力扣的算法題了解。涉及到的動(dòng)態(tài)規(guī)劃、二分、回朔等方式在評(píng)論都有大量的提到。他的目標(biāo)依然是為了減少真實(shí) DOM 的移動(dòng)次數(shù)

        https://leetcode.cn/problems/longest-increasing-subsequence/description/


        總結(jié)

        本文比較了 React 和 Vue 在 diff 算法上的相同的考慮與差異的處理,主要的作用是為了應(yīng)對(duì)面試中的場(chǎng)景,在實(shí)踐應(yīng)用場(chǎng)景中用處不是很大。大家可以收藏起來(lái)慢慢看。

        我在最后用了非常大量的篇幅介紹 React 與 Vue 在列表中對(duì)比的差異,Vue 由于在努力追求真實(shí) DOM 能夠以最小的移動(dòng)來(lái)更新 DOM,因此在算法上更加特殊。

        不過(guò)需要特別注意的是,在同一次事件循環(huán)中,由于瀏覽器在渲染時(shí)本身就會(huì)對(duì) DOM 樹(shù)進(jìn)行統(tǒng)一的處理和繪制,因此,節(jié)點(diǎn)移動(dòng) 1 次,還是移動(dòng) 100 次,只要在這一幀中的最終結(jié)果是一樣的,那么在渲染階段所消耗的計(jì)算成本實(shí)際上是一樣的,沒(méi)有太大的區(qū)別。因此,我個(gè)人的觀點(diǎn)是 Vue 這種算法對(duì)于渲染性能的提升效果應(yīng)該是非常微弱的。

        瀏覽 124
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一级片影视| 午夜国产理论 | xxxx日本高清18 | 91亚洲精品国偷拍自产在线视频 | 强 伦久久久久久久1234裸头 | 91人妻无码成人精品一区二区 | 欧美极品在线 | 男人呻吟双腿大开男男h互攻视频 | 91亚色 | 公妇借种乱h日出水了小说 |