摘要:通過對樹進(jìn)行層級(jí)控制,同一個(gè)父節(jié)點(diǎn)下的所有子節(jié)點(diǎn)。新老集合進(jìn)行差異化對比,發(fā)現(xiàn),則創(chuàng)建并插入至新集合,刪除老集合以此類推,創(chuàng)建并插入和,刪除和。
虛擬dom
Jsx 表面寫的是html,其實(shí)內(nèi)部執(zhí)行的是一段js
createElement
React.createElement( type, [props], [...children] )
createElement把這個(gè)樹形結(jié)構(gòu),存在內(nèi)存里面
Jsx最終以這樣的一個(gè)個(gè)對象遞歸的存在內(nèi)存中,執(zhí)行diff算法
多層結(jié)構(gòu)
簡單的createElement實(shí)現(xiàn)
reactElement - 生成的是一個(gè)對象來描述這個(gè)節(jié)點(diǎn)
與傳統(tǒng)樹的diff的區(qū)別
計(jì)算一棵樹形結(jié)構(gòu)轉(zhuǎn)換成另一棵樹形結(jié)構(gòu)的最少操作,是一個(gè)復(fù)雜且值得研究的問題。傳統(tǒng) diff 算法通過循環(huán)遞歸對節(jié)點(diǎn)進(jìn)行依次對比,效率低下,算法復(fù)雜度達(dá)到 O(n^3)
react diff策略
Web UI 中 DOM 節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作特別少,可以忽略不計(jì)。
擁有相同類的兩個(gè)組件將會(huì)生成相似的樹形結(jié)構(gòu),擁有不同類的兩個(gè)組件將會(huì)生成不同的樹形結(jié)構(gòu)。
對于同一層級(jí)的一組子節(jié)點(diǎn),它們可以通過唯一 id 進(jìn)行區(qū)分。
tree diff
基于策略一,對樹進(jìn)行分層比較,兩棵樹只會(huì)對同一層次的節(jié)點(diǎn)進(jìn)行比較。 React 通過 updateDepth 對 Virtual DOM 樹進(jìn)行層級(jí)控制,同一個(gè)父節(jié)點(diǎn)下的所有子節(jié)點(diǎn)。
什么是 DOM 節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作?
A 節(jié)點(diǎn)(包括其子節(jié)點(diǎn))整個(gè)被移動(dòng)到 D 節(jié)點(diǎn)下
如果出現(xiàn)了 DOM 節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作,React diff 會(huì)有怎樣的表現(xiàn)呢?
React 只會(huì)簡單的考慮同層級(jí)節(jié)點(diǎn)的位置變換,而對于不同層級(jí)的節(jié)點(diǎn),只有創(chuàng)建和刪除操作。
當(dāng)根節(jié)點(diǎn)發(fā)現(xiàn)子節(jié)點(diǎn)中 A 消失了,就會(huì)直接銷毀 A;當(dāng) D 發(fā)現(xiàn)多了一個(gè)子節(jié)點(diǎn) A,則會(huì)創(chuàng)建新的 A(包括子節(jié)點(diǎn))作為其子節(jié)點(diǎn)。此時(shí),React diff 的執(zhí)行情況:create A -> create B -> create C -> delete A。
注意:
在開發(fā)組件時(shí),保持穩(wěn)定的 DOM 結(jié)構(gòu)會(huì)有助于性能的提升。例如,可以通過 CSS 隱藏或顯示節(jié)點(diǎn),而不是真的移除或添加 DOM 節(jié)點(diǎn)。
component diff
依據(jù)策略二
如果是同一類型的組件,按照原策略繼續(xù)比較 virtual DOM tree。
如果不是,則將該組件判斷為 dirty component,從而替換整個(gè)組件下的所有子節(jié)點(diǎn)。
對于同一類型的組件,有可能其 Virtual DOM 沒有任何變化,如果能夠確切的知道這點(diǎn)那可以節(jié)省大量的 diff 運(yùn)算時(shí)間,因此 React 允許用戶通過 shouldComponentUpdate() 來判斷該組件是否需要進(jìn)行 diff。
React 判斷 D 和 G 是不同類型的組件,就不會(huì)比較二者的結(jié)構(gòu),而是直接刪除 component D,重新創(chuàng)建 component G 以及其子節(jié)點(diǎn),即使D 和 G的結(jié)構(gòu)很相似
element diff
當(dāng)節(jié)點(diǎn)處于同一層級(jí)時(shí),React diff 提供了三種節(jié)點(diǎn)操作,分別為:INSERT_MARKUP(插入)、MOVE_EXISTING(移動(dòng))和 REMOVE_NODE(刪除)。
INSERT_MARKUP,新的 component 類型不在老集合里, 即是全新的節(jié)點(diǎn),需要對新節(jié)點(diǎn)執(zhí)行插入操作。
MOVE_EXISTING,在老集合有新 component 類型,且 element 是可更新的類型,generateComponentChildren 已調(diào)用 receiveComponent,這種情況下 prevChild=nextChild,就需要做移動(dòng)操作,可以復(fù)用以前的 DOM 節(jié)點(diǎn)。
REMOVE_NODE,老 component 類型,在新集合里也有,但對應(yīng)的 element 不同則不能直接復(fù)用和更新,需要執(zhí)行刪除操作,或者老 component 不在新集合里的,也需要執(zhí)行刪除操作。
eg: 新老集合進(jìn)行 diff 差異化對比,發(fā)現(xiàn) B != A,則創(chuàng)建并插入 B 至新集合,刪除老集合 A;以此類推,創(chuàng)建并插入 A、D 和 C,刪除 B、C 和 D。
帶來的問題:都是相同的節(jié)點(diǎn),但由于位置發(fā)生變化,導(dǎo)致需要進(jìn)行繁雜低效的刪除、創(chuàng)建操作,其實(shí)只要對這些節(jié)點(diǎn)進(jìn)行位置移動(dòng)即可
react優(yōu)化策略:允許開發(fā)者對同一層級(jí)的同組子節(jié)點(diǎn),添加唯一 key 進(jìn)行區(qū)分
優(yōu)化后diff實(shí)現(xiàn):
對新集合的節(jié)點(diǎn)進(jìn)行循環(huán)遍歷,通過唯一 key 可以判斷新老集合中是否存在相同的節(jié)點(diǎn)
如果存在相同節(jié)點(diǎn),則進(jìn)行移動(dòng)操作,但在移動(dòng)前需要將當(dāng)前節(jié)點(diǎn)在老集合中的位置child._mountIndex與lastIndex(訪問過的節(jié)點(diǎn)在老集合中最右的位置即最大的位置)進(jìn)行比較,if (child._mountIndex < lastIndex),則進(jìn)行節(jié)點(diǎn)移動(dòng)操作
分析:
element _mountIndex lastIndex nextIndex enqueueMove B 1 0 0 false A 0 1 1 true D 3 1 2 false C 2 3 3 true
step:
從新集合中取得 B,判斷老集合中存在相同節(jié)點(diǎn) B B 在老集合中的位置 B._mountIndex = 1 初始 lastIndex = 0 不滿足 child._mountIndex < lastIndex 的條件,因此不對 B 進(jìn)行移動(dòng)操作 更新 lastIndex = Math.max(prevChild._mountIndex, lastIndex) lastIndex更新為1 將 B 的位置更新為新集合中的位置prevChild._mountIndex = nextIndex,此時(shí)新集合中 B._mountIndex = 0,nextIndex++
以上主要分析新老集合中存在相同節(jié)點(diǎn)但位置不同時(shí),對節(jié)點(diǎn)進(jìn)行位置移動(dòng)的情況,如果新集合中有新加入的節(jié)點(diǎn)且老集合存在需要?jiǎng)h除的節(jié)點(diǎn),那么 React diff 又是如何對比運(yùn)作的呢?
element _mountIndex lastIndex nextIndex enqueueMove B 1 0 0 false E no exist C 2 1 2 false A 0 2 3 true
step
新建:從新集合中取得 E,判斷老集合中不存在相同節(jié)點(diǎn) E,則創(chuàng)建新節(jié)點(diǎn) E lastIndex不做處理 E 的位置更新為新集合中的位置,nextIndex++ 刪除:當(dāng)完成新集合中所有節(jié)點(diǎn) diff 時(shí),最后還需要對老集合進(jìn)行循環(huán)遍歷,判斷是否存在新集合中沒有但老集合中仍存在的節(jié)點(diǎn),發(fā)現(xiàn)存在這樣的節(jié)點(diǎn) D,因此刪除節(jié)點(diǎn) D
react diff的問題
理論上 diff 應(yīng)該只需對 D 執(zhí)行移動(dòng)操作,然而由于 D 在老集合的位置是最大的,導(dǎo)致其他節(jié)點(diǎn)的 _mountIndex < lastIndex,造成 D 沒有執(zhí)行移動(dòng)操作,而是 A、B、C 全部移動(dòng)到 D 節(jié)點(diǎn)后面的現(xiàn)象
建議:在開發(fā)過程中,盡量減少類似將最后一個(gè)節(jié)點(diǎn)移動(dòng)到列表首部的操作,當(dāng)節(jié)點(diǎn)數(shù)量過大或更新操作過于頻繁時(shí),在一定程度上會(huì)影響 React 的渲染性能。
總結(jié):
React 通過制定大膽的 diff 策略,將 O(n3) 復(fù)雜度的問題轉(zhuǎn)換成 O(n) 復(fù)雜度的問題;
React 通過分層求異的策略,對 tree diff 進(jìn)行算法優(yōu)化;
React 通過相同類生成相似樹形結(jié)構(gòu),不同類生成不同樹形結(jié)構(gòu)的策略,對 component diff 進(jìn)行算法優(yōu)化;
React 通過設(shè)置唯一 key的策略,對 element diff 進(jìn)行算法優(yōu)化;
建議,在開發(fā)組件時(shí),保持穩(wěn)定的 DOM 結(jié)構(gòu)會(huì)有助于性能的提升;
建議,在開發(fā)過程中,盡量減少類似將最后一個(gè)節(jié)點(diǎn)移動(dòng)到列表首部的操作,當(dāng)節(jié)點(diǎn)數(shù)量過大或更新操作過于頻繁時(shí),在一定程度上會(huì)影響 React 的渲染性能。
https://zhuanlan.zhihu.com/p/...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98461.html
摘要:所以只針對同層級(jí)節(jié)點(diǎn)做比較,將復(fù)雜度的問題轉(zhuǎn)換成復(fù)雜度的問題。 React系列 React系列 --- 簡單模擬語法(一)React系列 --- Jsx, 合成事件與Refs(二)React系列 --- virtualdom diff算法實(shí)現(xiàn)分析(三)React系列 --- 從Mixin到HOC再到HOOKS(四)React系列 --- createElement, ReactElem...
摘要:而對比變化,找出需要更新部分的算法我們稱之為算法。整個(gè)系列大概會(huì)有四篇,我每周會(huì)更新一到兩篇,我會(huì)第一時(shí)間在上更新,有問題需要探討也請?jiān)谏匣貜?fù)我博客地址關(guān)注點(diǎn),訂閱點(diǎn)上一篇文章從零開始實(shí)現(xiàn)一個(gè)二組件和生命周期 前言 在上一篇文章,我們已經(jīng)實(shí)現(xiàn)了React的組件功能,從功能的角度來說已經(jīng)實(shí)現(xiàn)了React的核心功能了。 但是我們的實(shí)現(xiàn)方式有很大的問題:每次更新都重新渲染整個(gè)應(yīng)用或者整個(gè)組件...
摘要:可實(shí)際上并不是創(chuàng)造的,將這個(gè)概念拿過來以后融會(huì)貫通慢慢地成為目前前端最炙手可熱的框架之一。則是將再抽象一層生成的簡化版對象,這個(gè)對象也擁有上的一些屬性,比如等,但它是完全脫離于瀏覽器而存在的。所以今天我要手把手教大家怎么從零開始實(shí)現(xiàn)。 假如你的項(xiàng)目使用了React,你知道怎么做性能優(yōu)化嗎?你知道為什么React讓你寫shouldComponentUpdate或者React.PureCo...
摘要:父組件向子組件之間非常常見,通過機(jī)制傳遞即可。我們應(yīng)該聽說過高階函數(shù),這種函數(shù)接受函數(shù)作為輸入,或者是輸出一個(gè)函數(shù),比如以及等函數(shù)。在傳遞數(shù)據(jù)的時(shí)候,我們可以用進(jìn)一步提高性能。 本文主要談自己在react學(xué)習(xí)的過程中總結(jié)出來的一些經(jīng)驗(yàn)和資源,內(nèi)容邏輯參考了深入react技術(shù)棧一書以及網(wǎng)上的諸多資源,但也并非完全照抄,代碼基本都是自己實(shí)踐,主要為平時(shí)個(gè)人學(xué)習(xí)做一個(gè)總結(jié)和參考。 本文的關(guān)鍵...
閱讀 3244·2021-11-11 11:00
閱讀 2565·2019-08-29 11:23
閱讀 1441·2019-08-29 10:58
閱讀 2323·2019-08-29 10:58
閱讀 2952·2019-08-23 18:26
閱讀 2507·2019-08-23 18:18
閱讀 2038·2019-08-23 16:53
閱讀 3411·2019-08-23 13:13