目前衷快,前端領(lǐng)域中 React 勢頭正盛买喧,使用者眾多卻少有能夠深入剖析內(nèi)部實現(xiàn)機(jī)制和原理业筏。本系列文章希望通過剖析 React 源碼憔杨,理解其內(nèi)部的實現(xiàn)原理,知其然更要知其所以然蒜胖。
React diff 作為 Virtual DOM 的加速器消别,其算法上的改進(jìn)優(yōu)化是 React 整個界面渲染的基礎(chǔ),以及性能提高的保障台谢,同時也是 React 源碼中最神秘寻狂、最不可思議的部分,本文從源碼入手朋沮,深入剖析 React diff 的不可思議之處蛇券。
閱讀本文需要對 React 有一定的了解,如果你不知何為 React,請詳讀 React 官方文檔怀读。
如果你對 React diff 存在些許疑惑诉位,或者你對算法優(yōu)化感興趣,那么本文值得閱讀和討論菜枷。
前言
React 中最值得稱道的部分莫過于 Virtual DOM 與 diff 的完美結(jié)合苍糠,特別是其高效的 diff 算法,讓用戶可以無需顧忌性能問題而”任性自由”的刷新頁面啤誊,讓開發(fā)者也可以無需關(guān)心 Virtual DOM 背后的運(yùn)作原理岳瞭,因為 React diff 會幫助我們計算出 Virtual DOM 中真正變化的部分,并只針對該部分進(jìn)行實際 DOM 操作蚊锹,而非重新渲染整個頁面瞳筏,從而保證了每次操作更新后頁面的高效渲染,因此 Virtual DOM 與 diff 是保證 React 性能口碑的幕后推手牡昆。
行文至此姚炕,可能會有讀者質(zhì)疑:React 無非就是引入 diff 這一概念,且 diff 算法也并非其首創(chuàng)丢烘,何必吹噓的如此天花亂墜呢柱宦?
其實,正是因為 diff 算法的普識度高播瞳,就更應(yīng)該認(rèn)可 React 針對 diff 算法優(yōu)化所做的努力與貢獻(xiàn)掸刊,更能體現(xiàn) React 開發(fā)者們的魅力與智慧!
傳統(tǒng) diff 算法
計算一棵樹形結(jié)構(gòu)轉(zhuǎn)換成另一棵樹形結(jié)構(gòu)的最少操作赢乓,是一個復(fù)雜且值得研究的問題忧侧。傳統(tǒng) diff 算法通過循環(huán)遞歸對節(jié)點(diǎn)進(jìn)行依次對比,效率低下牌芋,算法復(fù)雜度達(dá)到 O(n^3)蚓炬,其中 n 是樹中節(jié)點(diǎn)的總數(shù)。O(n^3) 到底有多可怕姜贡,這意味著如果要展示1000個節(jié)點(diǎn)试吁,就要依次執(zhí)行上十億次的比較。這種指數(shù)型的性能消耗對于前端渲染場景來說代價太高了楼咳!現(xiàn)今的 CPU 每秒鐘能執(zhí)行大約30億條指令熄捍,即便是最高效的實現(xiàn),也不可能在一秒內(nèi)計算出差異情況母怜。
如果 React 只是單純的引入 diff 算法而沒有任何的優(yōu)化改進(jìn)余耽,那么其效率是遠(yuǎn)遠(yuǎn)無法滿足前端渲染所要求的性能。
因此苹熏,想要將 diff 思想引入 Virtual DOM碟贾,就需要設(shè)計一種穩(wěn)定高效的 diff 算法币喧,而 React 做到了!
那么袱耽,React diff 到底是如何實現(xiàn)的呢杀餐?
詳解 React diff
傳統(tǒng) diff 算法的復(fù)雜度為 O(n^3),顯然這是無法滿足性能要求的朱巨。React 通過制定大膽的策略史翘,將 O(n^3) 復(fù)雜度的問題轉(zhuǎn)換成 O(n) 復(fù)雜度的問題。
diff 策略
Web UI 中 DOM 節(jié)點(diǎn)跨層級的移動操作特別少冀续,可以忽略不計琼讽。
擁有相同類的兩個組件將會生成相似的樹形結(jié)構(gòu),擁有不同類的兩個組件將會生成不同的樹形結(jié)構(gòu)洪唐。
對于同一層級的一組子節(jié)點(diǎn)钻蹬,它們可以通過唯一 id 進(jìn)行區(qū)分。
基于以上三個前提策略凭需,React 分別對 tree diff问欠、component diff 以及 element diff 進(jìn)行算法優(yōu)化,事實也證明這三個前提策略是合理且準(zhǔn)確的功炮,它保證了整體界面構(gòu)建的性能溅潜。
tree diff
component diff
element diff
本文中源碼 ReactMultiChild.js
tree diff
基于策略一,React 對樹的算法進(jìn)行了簡潔明了的優(yōu)化薪伏,即對樹進(jìn)行分層比較,兩棵樹只會對同一層次的節(jié)點(diǎn)進(jìn)行比較粗仓。
既然 DOM 節(jié)點(diǎn)跨層級的移動操作少到可以忽略不計嫁怀,針對這一現(xiàn)象,React 通過 updateDepth 對 Virtual DOM 樹進(jìn)行層級控制借浊,只會對相同顏色方框內(nèi)的 DOM 節(jié)點(diǎn)進(jìn)行比較塘淑,即同一個父節(jié)點(diǎn)下的所有子節(jié)點(diǎn)。當(dāng)發(fā)現(xiàn)節(jié)點(diǎn)已經(jīng)不存在蚂斤,則該節(jié)點(diǎn)及其子節(jié)點(diǎn)會被完全刪除掉存捺,不會用于進(jìn)一步的比較。這樣只需要對樹進(jìn)行一次遍歷曙蒸,便能完成整個 DOM 樹的比較捌治。
updateChildren: function(nextNestedChildrenElements, transaction, context) {
updateDepth++;
var errorThrown = true;
try {
this._updateChildren(nextNestedChildrenElements, transaction, context);
errorThrown = false;
} finally {
updateDepth--;
if (!updateDepth) {
if (errorThrown) {
clearQueue();
} else {
processQueue();
}
}
}
}
分析至此,大部分人可能都存在這樣的疑問:如果出現(xiàn)了 DOM 節(jié)點(diǎn)跨層級的移動操作纽窟,React diff 會有怎樣的表現(xiàn)呢肖油?是的,對此我也好奇不已臂港,不如試驗一番森枪。
如下圖视搏,A 節(jié)點(diǎn)(包括其子節(jié)點(diǎn))整個被移動到 D 節(jié)點(diǎn)下,由于 React 只會簡單的考慮同層級節(jié)點(diǎn)的位置變換县袱,而對于不同層級的節(jié)點(diǎn)浑娜,只有創(chuàng)建和刪除操作。當(dāng)根節(jié)點(diǎn)發(fā)現(xiàn)子節(jié)點(diǎn)中 A 消失了式散,就會直接銷毀 A筋遭;當(dāng) D 發(fā)現(xiàn)多了一個子節(jié)點(diǎn) A,則會創(chuàng)建新的 A(包括子節(jié)點(diǎn))作為其子節(jié)點(diǎn)杂数。此時宛畦,React diff 的執(zhí)行情況:create A -> create B -> create C -> delete A。
由此可發(fā)現(xiàn)揍移,當(dāng)出現(xiàn)節(jié)點(diǎn)跨層級移動時次和,并不會出現(xiàn)想象中的移動操作,而是以 A 為根節(jié)點(diǎn)的樹被整個重新創(chuàng)建那伐,這是一種影響 React 性能的操作踏施,因此 React 官方建議不要進(jìn)行 DOM 節(jié)點(diǎn)跨層級的操作。
注意:在開發(fā)組件時罕邀,保持穩(wěn)定的 DOM 結(jié)構(gòu)會有助于性能的提升畅形。例如,可以通過 CSS 隱藏或顯示節(jié)點(diǎn)诉探,而不是真的移除或添加 DOM 節(jié)點(diǎn)日熬。
component diff
React 是基于組件構(gòu)建應(yīng)用的,對于組件間的比較所采取的策略也是簡潔高效肾胯。
如果是同一類型的組件竖席,按照原策略繼續(xù)比較 virtual DOM tree。
如果不是敬肚,則將該組件判斷為 dirty component毕荐,從而替換整個組件下的所有子節(jié)點(diǎn)。
對于同一類型的組件艳馒,有可能其 Virtual DOM 沒有任何變化憎亚,如果能夠確切的知道這點(diǎn)那可以節(jié)省大量的 diff 運(yùn)算時間,因此 React 允許用戶通過 shouldComponentUpdate() 來判斷該組件是否需要進(jìn)行 diff弄慰。
如下圖第美,當(dāng) component D 改變?yōu)?component G 時,即使這兩個 component 結(jié)構(gòu)相似曹动,一旦 React 判斷 D 和 G 是不同類型的組件斋日,就不會比較二者的結(jié)構(gòu),而是直接刪除 component D墓陈,重新創(chuàng)建 component G 以及其子節(jié)點(diǎn)恶守。雖然當(dāng)兩個 component 是不同類型但結(jié)構(gòu)相似時第献,React diff 會影響性能,但正如 React 官方博客所言:不同類型的 component 是很少存在相似 DOM tree 的機(jī)會兔港,因此這種極端因素很難在實現(xiàn)開發(fā)過程中造成重大影響的庸毫。
element diff
當(dāng)節(jié)點(diǎn)處于同一層級時,React diff 提供了三種節(jié)點(diǎn)操作衫樊,分別為:INSERT_MARKUP(插入)飒赃、MOVE_EXISTING(移動)和 REMOVE_NODE(刪除)。
INSERT_MARKUP科侈,新的 component 類型不在老集合里载佳, 即是全新的節(jié)點(diǎn),需要對新節(jié)點(diǎn)執(zhí)行插入操作臀栈。
MOVE_EXISTING蔫慧,在老集合有新 component 類型,且 element 是可更新的類型权薯,generateComponentChildren 已調(diào)用 receiveComponent姑躲,這種情況下 prevChild=nextChild,就需要做移動操作盟蚣,可以復(fù)用以前的 DOM 節(jié)點(diǎn)黍析。
REMOVE_NODE,老 component 類型屎开,在新集合里也有阐枣,但對應(yīng)的 element 不同則不能直接復(fù)用和更新,需要執(zhí)行刪除操作奄抽,或者老 component 不在新集合里的侮繁,也需要執(zhí)行刪除操作。
function enqueueInsertMarkup(parentInst, markup, toIndex) {
updateQueue.push({
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.INSERT_MARKUP,
markupIndex: markupQueue.push(markup) - 1,
content: null,
fromIndex: null,
toIndex: toIndex,
});
}
function enqueueMove(parentInst, fromIndex, toIndex) {
updateQueue.push({
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.MOVE_EXISTING,
markupIndex: null,
content: null,
fromIndex: fromIndex,
toIndex: toIndex,
});
}
function enqueueRemove(parentInst, fromIndex) {
updateQueue.push({
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.REMOVE_NODE,
markupIndex: null,
content: null,
fromIndex: fromIndex,
toIndex: null,
});
}
如下圖如孝,老集合中包含節(jié)點(diǎn):A、B娩贷、C第晰、D,更新后的新集合中包含節(jié)點(diǎn):B彬祖、A茁瘦、D、C储笑,此時新老集合進(jìn)行 diff 差異化對比甜熔,發(fā)現(xiàn) B != A,則創(chuàng)建并插入 B 至新集合突倍,刪除老集合 A腔稀;以此類推盆昙,創(chuàng)建并插入 A、D 和 C焊虏,刪除 B淡喜、C 和 D。
React 發(fā)現(xiàn)這類操作繁瑣冗余诵闭,因為這些都是相同的節(jié)點(diǎn)炼团,但由于位置發(fā)生變化,導(dǎo)致需要進(jìn)行繁雜低效的刪除疏尿、創(chuàng)建操作瘟芝,其實只要對這些節(jié)點(diǎn)進(jìn)行位置移動即可。
針對這一現(xiàn)象褥琐,React 提出優(yōu)化策略:允許開發(fā)者對同一層級的同組子節(jié)點(diǎn)锌俱,添加唯一 key 進(jìn)行區(qū)分,雖然只是小小的改動踩衩,性能上卻發(fā)生了翻天覆地的變化嚼鹉!
新老集合所包含的節(jié)點(diǎn),如下圖所示驱富,新老集合進(jìn)行 diff 差異化對比锚赤,通過 key 發(fā)現(xiàn)新老集合中的節(jié)點(diǎn)都是相同的節(jié)點(diǎn),因此無需進(jìn)行節(jié)點(diǎn)刪除和創(chuàng)建褐鸥,只需要將老集合中節(jié)點(diǎn)的位置進(jìn)行移動线脚,更新為新集合中節(jié)點(diǎn)的位置,此時 React 給出的 diff 結(jié)果為:B叫榕、D 不做任何操作浑侥,A、C 進(jìn)行移動操作晰绎,即可寓落。
那么,如此高效的 diff 到底是如何運(yùn)作的呢荞下?讓我們通過源碼進(jìn)行詳細(xì)分析伶选。
首先對新集合的節(jié)點(diǎn)進(jìn)行循環(huán)遍歷,for (name in nextChildren)尖昏,通過唯一 key 可以判斷新老集合中是否存在相同的節(jié)點(diǎn)仰税,if (prevChild === nextChild),如果存在相同節(jié)點(diǎn)抽诉,則進(jìn)行移動操作陨簇,但在移動前需要將當(dāng)前節(jié)點(diǎn)在老集合中的位置與 lastIndex 進(jìn)行比較,if (child._mountIndex < lastIndex)迹淌,則進(jìn)行節(jié)點(diǎn)移動操作河绽,否則不執(zhí)行該操作己单。這是一種順序優(yōu)化手段,lastIndex 一直在更新葵姥,表示訪問過的節(jié)點(diǎn)在老集合中最右的位置(即最大的位置)荷鼠,如果新集合中當(dāng)前訪問的節(jié)點(diǎn)比 lastIndex 大,說明當(dāng)前訪問節(jié)點(diǎn)在老集合中就比上一個節(jié)點(diǎn)位置靠后榔幸,則該節(jié)點(diǎn)不會影響其他節(jié)點(diǎn)的位置允乐,因此不用添加到差異隊列中,即不執(zhí)行移動操作削咆,只有當(dāng)訪問的節(jié)點(diǎn)比 lastIndex 小時牍疏,才需要進(jìn)行移動操作。
以上圖為例拨齐,可以更為清晰直觀的描述 diff 的差異對比過程:
從新集合中取得 B鳞陨,判斷老集合中存在相同節(jié)點(diǎn) B,通過對比節(jié)點(diǎn)位置判斷是否進(jìn)行移動操作瞻惋,B 在老集合中的位置 B._mountIndex = 1厦滤,此時 lastIndex = 0,不滿足 child._mountIndex < lastIndex 的條件歼狼,因此不對 B 進(jìn)行移動操作掏导;更新 lastIndex = Math.max(prevChild._mountIndex, lastIndex),其中 prevChild._mountIndex 表示 B 在老集合中的位置羽峰,則 lastIndex = 1趟咆,并將 B 的位置更新為新集合中的位置prevChild._mountIndex = nextIndex,此時新集合中 B._mountIndex = 0梅屉,nextIndex++ 進(jìn)入下一個節(jié)點(diǎn)的判斷值纱。
從新集合中取得 A,判斷老集合中存在相同節(jié)點(diǎn) A坯汤,通過對比節(jié)點(diǎn)位置判斷是否進(jìn)行移動操作虐唠,A 在老集合中的位置 A._mountIndex = 0,此時 lastIndex = 1惰聂,滿足 child._mountIndex < lastIndex的條件凿滤,因此對 A 進(jìn)行移動操作enqueueMove(this, child._mountIndex, toIndex),其中 toIndex 其實就是 nextIndex庶近,表示 A 需要移動到的位置;更新 lastIndex = Math.max(prevChild._mountIndex, lastIndex)眷蚓,則 lastIndex = 1鼻种,并將 A 的位置更新為新集合中的位置 prevChild._mountIndex = nextIndex,此時新集合中A._mountIndex = 1沙热,nextIndex++ 進(jìn)入下一個節(jié)點(diǎn)的判斷叉钥。
從新集合中取得 D罢缸,判斷老集合中存在相同節(jié)點(diǎn) D,通過對比節(jié)點(diǎn)位置判斷是否進(jìn)行移動操作投队,D 在老集合中的位置 D._mountIndex = 3枫疆,此時 lastIndex = 1,不滿足 child._mountIndex < lastIndex的條件敷鸦,因此不對 D 進(jìn)行移動操作息楔;更新 lastIndex = Math.max(prevChild._mountIndex, lastIndex),則 lastIndex = 3扒披,并將 D 的位置更新為新集合中的位置 prevChild._mountIndex = nextIndex值依,此時新集合中D._mountIndex = 2,nextIndex++ 進(jìn)入下一個節(jié)點(diǎn)的判斷碟案。
從新集合中取得 C愿险,判斷老集合中存在相同節(jié)點(diǎn) C,通過對比節(jié)點(diǎn)位置判斷是否進(jìn)行移動操作价说,C 在老集合中的位置 C._mountIndex = 2辆亏,此時 lastIndex = 3,滿足 child._mountIndex < lastIndex 的條件鳖目,因此對 C 進(jìn)行移動操作 enqueueMove(this, child._mountIndex, toIndex)扮叨;更新 lastIndex = Math.max(prevChild._mountIndex, lastIndex),則 lastIndex = 3疑苔,并將 C 的位置更新為新集合中的位置 prevChild._mountIndex = nextIndex甫匹,此時新集合中 C._mountIndex = 3,nextIndex++ 進(jìn)入下一個節(jié)點(diǎn)的判斷惦费,由于 C 已經(jīng)是最后一個節(jié)點(diǎn)兵迅,因此 diff 到此完成。
以上主要分析新老集合中存在相同節(jié)點(diǎn)但位置不同時薪贫,對節(jié)點(diǎn)進(jìn)行位置移動的情況恍箭,如果新集合中有新加入的節(jié)點(diǎn)且老集合存在需要刪除的節(jié)點(diǎn),那么 React diff 又是如何對比運(yùn)作的呢瞧省?
以下圖為例:
從新集合中取得 B扯夭,判斷老集合中存在相同節(jié)點(diǎn) B,由于 B 在老集合中的位置 B._mountIndex = 1鞍匾,此時lastIndex = 0交洗,因此不對 B 進(jìn)行移動操作;更新 lastIndex = 1橡淑,并將 B 的位置更新為新集合中的位置B._mountIndex = 0构拳,nextIndex++進(jìn)入下一個節(jié)點(diǎn)的判斷。
從新集合中取得 E,判斷老集合中不存在相同節(jié)點(diǎn) E置森,則創(chuàng)建新節(jié)點(diǎn) E斗埂;更新 lastIndex = 1,并將 E 的位置更新為新集合中的位置凫海,nextIndex++進(jìn)入下一個節(jié)點(diǎn)的判斷呛凶。
從新集合中取得 C,判斷老集合中存在相同節(jié)點(diǎn) C行贪,由于 C 在老集合中的位置C._mountIndex = 2漾稀,lastIndex = 1,此時 C._mountIndex > lastIndex瓮顽,因此不對 C 進(jìn)行移動操作县好;更新 lastIndex = 2,并將 C 的位置更新為新集合中的位置暖混,nextIndex++ 進(jìn)入下一個節(jié)點(diǎn)的判斷缕贡。
從新集合中取得 A,判斷老集合中存在相同節(jié)點(diǎn) A拣播,由于 A 在老集合中的位置A._mountIndex = 0晾咪,lastIndex = 2,此時 A._mountIndex < lastIndex贮配,因此對 A 進(jìn)行移動操作谍倦;更新 lastIndex = 2,并將 A 的位置更新為新集合中的位置泪勒,nextIndex++ 進(jìn)入下一個節(jié)點(diǎn)的判斷昼蛀。
當(dāng)完成新集合中所有節(jié)點(diǎn) diff 時,最后還需要對老集合進(jìn)行循環(huán)遍歷圆存,判斷是否存在新集合中沒有但老集合中仍存在的節(jié)點(diǎn)叼旋,發(fā)現(xiàn)存在這樣的節(jié)點(diǎn) D,因此刪除節(jié)點(diǎn) D沦辙,到此 diff 全部完成夫植。
_updateChildren: function(nextNestedChildrenElements, transaction, context) {
var prevChildren = this._renderedChildren;
var nextChildren = this._reconcilerUpdateChildren(
prevChildren, nextNestedChildrenElements, transaction, context
);
if (!nextChildren && !prevChildren) {
return;
}
var name;
var lastIndex = 0;
var nextIndex = 0;
for (name in nextChildren) {
if (!nextChildren.hasOwnProperty(name)) {
continue;
}
var prevChild = prevChildren && prevChildren[name];
var nextChild = nextChildren[name];
if (prevChild === nextChild) {
// 移動節(jié)點(diǎn)
this.moveChild(prevChild, nextIndex, lastIndex);
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
prevChild._mountIndex = nextIndex;
} else {
if (prevChild) {
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
// 刪除節(jié)點(diǎn)
this._unmountChild(prevChild);
}
// 初始化并創(chuàng)建節(jié)點(diǎn)
this._mountChildAtIndex(
nextChild, nextIndex, transaction, context
);
}
nextIndex++;
}
for (name in prevChildren) {
if (prevChildren.hasOwnProperty(name) &&
!(nextChildren && nextChildren.hasOwnProperty(name))) {
this._unmountChild(prevChildren[name]);
}
}
this._renderedChildren = nextChildren;
},
// 移動節(jié)點(diǎn)
moveChild: function(child, toIndex, lastIndex) {
if (child._mountIndex < lastIndex) {
this.prepareToManageChildren();
enqueueMove(this, child._mountIndex, toIndex);
}
},
// 創(chuàng)建節(jié)點(diǎn)
createChild: function(child, mountImage) {
this.prepareToManageChildren();
enqueueInsertMarkup(this, mountImage, child._mountIndex);
},
// 刪除節(jié)點(diǎn)
removeChild: function(child) {
this.prepareToManageChildren();
enqueueRemove(this, child._mountIndex);
},
_unmountChild: function(child) {
this.removeChild(child);
child._mountIndex = null;
},
_mountChildAtIndex: function(
child,
index,
transaction,
context) {
var mountImage = ReactReconciler.mountComponent(
child,
transaction,
this,
this._nativeContainerInfo,
context
);
child._mountIndex = index;
this.createChild(child, mountImage);
},
當(dāng)然,React diff 還是存在些許不足與待優(yōu)化的地方油讯,如下圖所示详民,若新集合的節(jié)點(diǎn)更新為:D、A陌兑、B沈跨、C,與老集合對比只有 D 節(jié)點(diǎn)移動兔综,而 A谒出、B隅俘、C 仍然保持原有的順序,理論上 diff 應(yīng)該只需對 D 執(zhí)行移動操作笤喳,然而由于 D 在老集合的位置是最大的,導(dǎo)致其他節(jié)點(diǎn)的 _mountIndex < lastIndex碌宴,造成 D 沒有執(zhí)行移動操作杀狡,而是 A、B贰镣、C 全部移動到 D 節(jié)點(diǎn)后面的現(xiàn)象呜象。
在此,讀者們可以討論思考:如何優(yōu)化上述問題碑隆?
建議:在開發(fā)過程中恭陡,盡量減少類似將最后一個節(jié)點(diǎn)移動到列表首部的操作,當(dāng)節(jié)點(diǎn)數(shù)量過大或更新操作過于頻繁時上煤,在一定程度上會影響 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ā)組件時荞膘,保持穩(wěn)定的 DOM 結(jié)構(gòu)會有助于性能的提升罚随;
建議,在開發(fā)過程中衫画,盡量減少類似將最后一個節(jié)點(diǎn)移動到列表首部的操作毫炉,當(dāng)節(jié)點(diǎn)數(shù)量過大或更新操作過于頻繁時,在一定程度上會影響 React 的渲染性能削罩。
參考資料
如果本文能夠為你解決些許關(guān)于 React diff 算法的疑惑瞄勾,請點(diǎn)個贊吧!