欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React之虛擬DOM的實現(xiàn)原理

 更新時間:2023年01月16日 14:44:10   作者:huangpb0624  
這篇文章主要介紹了React之虛擬DOM的實現(xiàn)原理分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

React虛擬DOM機(jī)制

  • 虛擬DOM本質(zhì)上是JavaScript對象,是對真實DOM的抽象
  • 狀態(tài)變更時,記錄新樹和舊樹的差異
  • 最后把差異更新到真正的dom中

React引入了虛擬DOM(Virtual DOM)的機(jī)制:在瀏覽器端用Javascript實現(xiàn)了一套DOM API。

基于React進(jìn)行開發(fā)時所有的DOM構(gòu)造都是通過虛擬DOM進(jìn)行,每當(dāng)數(shù)據(jù)變化時,React都會重新構(gòu)建整個DOM樹,然后React將當(dāng)前整個DOM樹和上一次的DOM樹進(jìn)行對比,得到DOM結(jié)構(gòu)的區(qū)別,然后僅僅將需要變化的部分進(jìn)行實際的瀏覽器DOM更新。

而且React能夠批量處理虛擬DOM的刷新,在一個事件循環(huán)(Event Loop)內(nèi)的兩次數(shù)據(jù)變化會被合并,例如你連續(xù)的先將節(jié)點內(nèi)容從A變成B,然后又從B變成A,React會認(rèn)為UI不發(fā)生任何變化。

盡管每一次都需要構(gòu)造完整的虛擬DOM樹,但是因為虛擬DOM是內(nèi)存數(shù)據(jù),性能是極高的,而對實際DOM進(jìn)行操作的僅僅是Diff部分,因而能達(dá)到提高性能的目的。這樣,在保證性能的同時,開發(fā)者將不再需要關(guān)注某個數(shù)據(jù)的變化如何更新到一個或多個具體的DOM元素,而只需要關(guān)心在任意一個數(shù)據(jù)狀態(tài)下,整個界面是如何Render的。

總之一句話:根據(jù) React 的設(shè)計,所有的 DOM 變動,都先在虛擬 DOM 上發(fā)生,然后再將實際發(fā)生變動的部分,反映在真實 DOM上

React diff 算法

diff 算法作為Virtual DOM的加速器,其算法的改進(jìn)優(yōu)化是React整個界面渲染的基礎(chǔ)和性能的保障,同時也是React源碼中最神秘的,最不可思議的部分。

1. 傳統(tǒng) diff 算法

計算一棵樹形結(jié)構(gòu)轉(zhuǎn)換為另一棵樹形結(jié)構(gòu)需要最少步驟,如果使用傳統(tǒng)的diff算法通過循環(huán)遞歸遍歷節(jié)點進(jìn)行對比,其復(fù)雜度要達(dá)到O(n^3),其中n是節(jié)點總數(shù),效率十分低下,假設(shè)我們要展示1000個節(jié)點,那么我們就要依次執(zhí)行上十億次的比較。

下面附上一則簡單的傳統(tǒng)diff算法:

let result = [];
// 比較葉子節(jié)點
const diffLeafs = function (beforeLeaf, afterLeaf) {
    // 獲取較大節(jié)點樹的長度
    let count = Math.max(beforeLeaf.children.length, afterLeaf.children.length);
    // 循環(huán)遍歷
    for (let i = 0; i < count; i++) {
        const beforeTag = beforeLeaf.children[i];
        const afterTag = afterLeaf.children[i];
        // 添加 afterTag 節(jié)點
        if (beforeTag === undefined) {
            result.push({ type: "add", element: afterTag });
            // 刪除 beforeTag 節(jié)點
        } else if (afterTag === undefined) {
            result.push({ type: "remove", element: beforeTag });
            // 節(jié)點名改變時,刪除 beforeTag 節(jié)點,添加 afterTag 節(jié)點
        } else if (beforeTag.tagName !== afterTag.tagName) {
            result.push({ type: "remove", element: beforeTag });
            result.push({ type: "add", element: afterTag });
            // 節(jié)點不變而內(nèi)容改變時,改變節(jié)點
        } else if (beforeTag.innerHTML !== afterTag.innerHTML) {
            if (beforeTag.children.length === 0) {
                result.push({
                    type: "changed",
                    beforeElement: beforeTag,
                    afterElement: afterTag,
                    html: afterTag.innerHTML
                });
            } else {
                // 遞歸比較
                diffLeafs(beforeTag, afterTag);
            }
        }
    }
    return result;
}

2. react diff 算法

1. diff 策略

下面介紹一下react diff算法的3個策略

  • Web UI 中DOM節(jié)點跨層級的移動操作特別少,可以忽略不計
  • 擁有相同類的兩個組件將會生成相似的樹形結(jié)構(gòu),擁有不同類的兩個組件將會生成不同的樹形結(jié)構(gòu)。
  • 對于同一層級的一組子節(jié)點,它們可以通過唯一id進(jìn)行區(qū)分。

對于以上三個策略,react 分別對 tree diff, component diff, element diff 進(jìn)行算法優(yōu)化。

2. tree diff

基于策略一,WebUI中DOM節(jié)點跨層級的移動操作少的可以忽略不計,React對Virtual DOM樹進(jìn)行層級控制,只會對相同層級的DOM節(jié)點進(jìn)行比較,即同一個父元素下的所有子節(jié)點,當(dāng)發(fā)現(xiàn)節(jié)點已經(jīng)不存在了,則會刪除掉該節(jié)點下所有的子節(jié)點,不會再進(jìn)行比較。這樣只需要對DOM樹進(jìn)行一次遍歷,就可以完成整個樹的比較。復(fù)雜度變?yōu)镺(n);

疑問:當(dāng)我們的DOM節(jié)點進(jìn)行跨層級操作時,diff 會有怎么樣的表現(xiàn)呢?

如下圖所示,A節(jié)點及其子節(jié)點被整個移動到D節(jié)點下面去,由于React只會簡單的考慮同級節(jié)點的位置變換,而對于不同層級的節(jié)點,只有創(chuàng)建和刪除操作,所以當(dāng)根節(jié)點發(fā)現(xiàn)A節(jié)點消失了,就會刪除A節(jié)點及其子節(jié)點,當(dāng)D發(fā)現(xiàn)多了一個子節(jié)點A,就會創(chuàng)建新的A作為其子節(jié)點。 

此時,diff的執(zhí)行情況是:

createA-->createB-->createC-->deleteA

由此可以發(fā)現(xiàn),當(dāng)出現(xiàn)節(jié)點跨層級移動時,并不會出現(xiàn)想象中的移動操作,而是會進(jìn)行刪除,重新創(chuàng)建的動作,這是一種很影響React性能的操作。因此官方也不建議進(jìn)行DOM節(jié)點跨層級的操作。

3. component diff

  • React是基于組件構(gòu)建應(yīng)用的,對于組件間的比較所采用的策略也是非常簡潔和高效的。
  • 如果是同一個類型的組件,則按照原策略進(jìn)行Virtual DOM比較。
  • 如果不是同一類型的組件,則將其判斷為dirty component,從而替換整個組價下的所有子節(jié)點。
  • 如果是同一個類型的組件,有可能經(jīng)過一輪Virtual DOM比較下來,并沒有發(fā)生變化。如果我們能夠提前確切知道這一點,那么就可以省下大量的diff運算時間。因此,React允許用戶通過shouldComponentUpdate()來判斷該組件是否需要進(jìn)行diff算法分析。

如下圖所示,當(dāng)組件D變?yōu)榻M件G時,即使這兩個組件結(jié)構(gòu)相似,一旦React判斷D和G是不用類型的組件,就不會比較兩者的結(jié)構(gòu),而是直接刪除組件D,重新創(chuàng)建組件G及其子節(jié)點。

雖然當(dāng)兩個組件是不同類型但結(jié)構(gòu)相似時,進(jìn)行diff算法分析會影響性能,但是畢竟不同類型的組件存在相似DOM樹的情況在實際開發(fā)過程中很少出現(xiàn),因此這種極端因素很難在實際開發(fā)過程中造成重大影響。 

4. element diff

當(dāng)節(jié)點屬于同一層級時,diff提供了3種節(jié)點操作,分別為INSERT_MARKUP(插入),MOVE_EXISTING(移動),REMOVE_NODE(刪除)。

  • INSERT_MARKUP:新的組件類型不在舊集合中,即全新的節(jié)點,需要對新節(jié)點進(jìn)行插入操作。
  • MOVE_EXISTING:舊集合中有新組件類型,且element是可更新的類型,這時候就需要做移動操作,可以復(fù)用以前的DOM節(jié)點。
  • REMOVE_NODE:舊組件類型,在新集合里也有,但對應(yīng)的element不同則不能直接復(fù)用和更新,需要執(zhí)行刪除操作,或者舊組件不在新集合里的,也需要執(zhí)行刪除操作。 

總結(jié)

通過diff策略,將算法從O(n^3)簡化為O(n)。

分層求異,對tree diff進(jìn)行優(yōu)化。

分組件求異,相同類生成相似樹形結(jié)構(gòu)、不同類生成不同樹形結(jié)構(gòu),對component diff進(jìn)行優(yōu)化。

設(shè)置key,對element diff進(jìn)行優(yōu)化。

盡量保持穩(wěn)定的DOM結(jié)構(gòu)、避免將最后一個節(jié)點移動到列表首部、避免節(jié)點數(shù)量過大或更新過于頻繁。

最后

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 基于antd的autocomplete的二次封裝查詢示例

    基于antd的autocomplete的二次封裝查詢示例

    這篇文章主要為大家介紹了基于antd的autocomplete的二次封裝查詢示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • 詳解如何在React中有效地監(jiān)聽鍵盤事件

    詳解如何在React中有效地監(jiān)聽鍵盤事件

    React是一種流行的JavaScript庫,用于構(gòu)建用戶界面,它提供了一種簡單而靈活的方式來創(chuàng)建交互式的Web應(yīng)用程序,在React中,我們經(jīng)常需要監(jiān)聽用戶的鍵盤事件,以便根據(jù)用戶的輸入做出相應(yīng)的反應(yīng),本文將向您介紹如何在React中有效地監(jiān)聽鍵盤事件,并展示一些常見的應(yīng)用場景
    2023-11-11
  • 深入研究React中setState源碼

    深入研究React中setState源碼

    這篇文章主要介紹了深入研究React中setState源碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • React實現(xiàn)合成事件的源碼分析

    React實現(xiàn)合成事件的源碼分析

    React?中的事件,是對原生事件的封裝,叫做合成事件。抽象出一層合成事件,是為了做兼容,抹平不同瀏覽器之間的差異。本文將從事件綁定和事件觸發(fā)角度,帶大家解讀下源碼,感興趣的可以了解一下
    2022-12-12
  • ReactNative點擊事件.bind(this)操作分析

    ReactNative點擊事件.bind(this)操作分析

    這篇文章主要為大家介紹了ReactNative點擊事件.bind(this)操作分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • React實現(xiàn)Excel文件的導(dǎo)出與在線預(yù)覽功能

    React實現(xiàn)Excel文件的導(dǎo)出與在線預(yù)覽功能

    這篇文章主要為大家詳細(xì)介紹了如何利用?React?18?的強(qiáng)大功能,演示如何使用?React?18?編寫?Excel?文件的導(dǎo)出與在線預(yù)覽功能,需要的小伙伴可以參考下
    2023-12-12
  • react性能優(yōu)化達(dá)到最大化的方法 immutable.js使用的必要性

    react性能優(yōu)化達(dá)到最大化的方法 immutable.js使用的必要性

    這篇文章主要為大家詳細(xì)介紹了react性能優(yōu)化達(dá)到最大化的方法,一步一步優(yōu)化react性能的過程,告訴大家使用immutable.js的必要性,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • React教程之Props驗證的具體用法(Props Validation)

    React教程之Props驗證的具體用法(Props Validation)

    這篇文章主要介紹了React教程之Props驗證的具體用法(Props Validation),非常具有實用價值,需要的朋友可以參考下
    2017-09-09
  • React入門教程之Hello World以及環(huán)境搭建詳解

    React入門教程之Hello World以及環(huán)境搭建詳解

    Facebook 為了開發(fā)一套更好更適合自己的JavaScript MVC 框架,所以產(chǎn)生了react。后來反響很好,所以于2013年5月開源。下面這篇文章主要給大家介紹了關(guān)于React入門教程之Hello World以及環(huán)境搭建的相關(guān)資料,需要的朋友可以參考借鑒。
    2017-07-07
  • React降級配置及Ant Design配置詳解

    React降級配置及Ant Design配置詳解

    這篇文章主要介紹了React降級配置及Ant Design配置詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12

最新評論