1、setState機(jī)制
理想情況下:
setState是異步的泳猬,調(diào)用setState只會(huì)提交一次state修改到隊(duì)列中,不會(huì)直接修改this.state宇植。等到滿足一定條件時(shí)得封,react會(huì)合并隊(duì)列中的所有修改,觸發(fā)一次update流程指郁,更新this.state
由于setState會(huì)觸發(fā)update過(guò)程忙上,因此在update過(guò)程中必經(jīng)的生命周期中調(diào)用setState會(huì)存在循環(huán)調(diào)用的風(fēng)險(xiǎn),另外用于監(jiān)聽state更新完成坡氯,可以使用setState方法的第二個(gè)參數(shù)晨横,回調(diào)函數(shù)。在這個(gè)回調(diào)中讀取this.state就是已經(jīng)批量更新后的結(jié)果
特殊情況下:
(1)mount流程中調(diào)用setState箫柳,不會(huì)進(jìn)入update流程手形,隊(duì)列在mount時(shí)合并修改并render
(2)setTimeout/Promise回調(diào)中調(diào)用setState,將不會(huì)進(jìn)行隊(duì)列的批更新悯恍,而是直接觸發(fā)一次update流程
2库糠、diff算法
三條策略:
(1)WebUI中DOM節(jié)點(diǎn)跨節(jié)點(diǎn)的操作特別少,可以忽略不計(jì)
(2)擁有相同類的組件會(huì)擁有相似的DOM結(jié)構(gòu)。擁有不同類的組件會(huì)生成不同的DOM結(jié)構(gòu)瞬欧。
(3)同一層級(jí)的子節(jié)點(diǎn)贷屎,可以根據(jù)唯一的ID來(lái)區(qū)分
針對(duì)這三個(gè)策略,react diff實(shí)施的具體策略是:
(1)diff對(duì)樹進(jìn)行分層比較艘虎,只對(duì)比兩棵樹同級(jí)別的節(jié)點(diǎn)唉侄。跨層級(jí)移動(dòng)節(jié)點(diǎn)野建,將會(huì)導(dǎo)致節(jié)點(diǎn)刪除属划,重新插入,無(wú)法復(fù)用
(2)diff對(duì)組件進(jìn)行類比較候生,類相同的遞歸diff子節(jié)點(diǎn)同眯,不同的直接銷毀重建。diff對(duì)同一層級(jí)的子節(jié)點(diǎn)進(jìn)行處理時(shí)唯鸭,會(huì)根據(jù)key進(jìn)行簡(jiǎn)要的復(fù)用须蜗。兩棵樹中存在相同key的節(jié)點(diǎn)時(shí),只會(huì)移動(dòng)節(jié)點(diǎn)
另外目溉,在對(duì)比同一層級(jí)的子節(jié)點(diǎn)時(shí)明肮,diff算法會(huì)以新樹的第一個(gè)子節(jié)點(diǎn)作為起點(diǎn)遍歷新樹,尋找舊樹種與之相同的節(jié)點(diǎn)缭付。如果節(jié)點(diǎn)存在晤愧,則移動(dòng)位置,如果不存在蛉腌,則新建一個(gè)節(jié)點(diǎn)。在這個(gè)過(guò)程中只厘,維護(hù)了一個(gè)字段lastIndex烙丛,這個(gè)字段表示已遍歷的所有新樹子節(jié)點(diǎn)在舊樹種最大的index。在移動(dòng)操作時(shí)羔味,只有舊的index小于lastIndex的才會(huì)移動(dòng)
3河咽、正確使用diff算法
(1)不適用跨層級(jí)移動(dòng)節(jié)點(diǎn)的操作
(2)對(duì)于條件渲染多個(gè)節(jié)點(diǎn)時(shí),盡量采用隱藏等方式切換節(jié)點(diǎn)赋元,而不是替換節(jié)點(diǎn)
(3)盡量避免將后面的子節(jié)點(diǎn)移動(dòng)到前面的操作忘蟹,當(dāng)節(jié)點(diǎn)數(shù)量較多時(shí),會(huì)產(chǎn)生一定的性能問(wèn)題
4搁凸、Fiber是什么媚值?有什么用?
在React15版本的時(shí)候护糖,如有組件要更新褥芒,則會(huì)遞歸向下遍歷整個(gè)虛擬DOM樹來(lái)判斷需要更新的地方。這種遞歸的方式弊端在于無(wú)法中斷嫡良,這要會(huì)造成如果我們需要更新一些龐大的組件锰扶,那么更新過(guò)程就會(huì)長(zhǎng)時(shí)間阻塞主線程献酗,從而造成用戶的交互、動(dòng)畫的更新等都不能及時(shí)響應(yīng)坷牛。
React組件更新過(guò)程簡(jiǎn)言之就是在持續(xù)調(diào)用函數(shù)的過(guò)程罕偎,這樣的過(guò)程會(huì)形成一個(gè)虛擬的調(diào)用棧。加入能控制這個(gè)調(diào)用棧的執(zhí)行京闰,把整個(gè)更新任務(wù)拆解開颜及,盡可能將更新任務(wù)放在瀏覽器空閑時(shí)間去執(zhí)行,那么就能解決以上問(wèn)題
Fiber重新實(shí)現(xiàn)了React的核心算法忙干,他有能力將整個(gè)更新任務(wù)拆分為一個(gè)個(gè)小的任務(wù)器予,并且能控制這些任務(wù)的執(zhí)行,主要包括兩個(gè)核心技術(shù):
(1)新的數(shù)據(jù)結(jié)構(gòu)fiber
fiber被認(rèn)為是一個(gè)工作單元捐迫,執(zhí)行更新任務(wù)的整個(gè)流程(不包括渲染)就是在反復(fù)尋找工作單元并運(yùn)行他們乾翔,實(shí)現(xiàn)拆分任務(wù)的功能。拆分成工作單元的目的就是為了能控制stack frame(調(diào)用棧中的內(nèi)容)施戴,可以隨時(shí)隨地執(zhí)行他們反浓,由此使得我們?cè)诿窟\(yùn)行一個(gè)工作單元后都可以按照情況繼續(xù)執(zhí)行或中斷工作(中斷的決定權(quán)在于調(diào)度器)。fiber內(nèi)部存儲(chǔ)了很多上下文信息赞哗,可以把它認(rèn)為是改進(jìn)版的虛擬DOM雷则,同樣也對(duì)應(yīng)了組件實(shí)例及DOM元素。同時(shí)fiber也會(huì)組成fiber tree肪笋,但結(jié)構(gòu)不再是一個(gè)樹形月劈,而是一個(gè)鏈表的結(jié)構(gòu)
```
{
…
//瀏覽器環(huán)境下指DOM節(jié)點(diǎn)
stateNode: any,
//形成列表結(jié)構(gòu)
return: Fiber | null,
child: Fiber | null,
sibling: Fiber | null,
//更新相關(guān)
pendingProps: any, //新的props
memoizedProps: any, //舊的props
//存儲(chǔ)setState中的第一個(gè)參數(shù)
updateQueue: UpdateQueue?| null,
memoizedState: any, //舊的state
//調(diào)度相關(guān)
expirationTime: ExpirationTime, //任務(wù)過(guò)期時(shí)間
//大部分情況下每個(gè)fiber都有一個(gè)替身fiber
//在更新過(guò)程中藤乙,所有操作都在替身上完成猜揪,當(dāng)渲染完成后,替身會(huì)替代本身
alternate: Fiber | null,
//先簡(jiǎn)單任務(wù)是更新DOM相關(guān)的內(nèi)容
effectTag: SideEffectTag,? //指這個(gè)節(jié)點(diǎn)需要進(jìn)行的DOM操作
//以下三個(gè)屬性也會(huì)形成一個(gè)鏈表
nextEffect: Fiber | null,? //下一個(gè)需要進(jìn)行DOM操作的節(jié)點(diǎn)
firstEffect: Fiber | null,? //第一個(gè)需要進(jìn)行DOM操作的節(jié)點(diǎn)
lastEffect: Fiber | null,? //最后一個(gè)需要進(jìn)行DOM操作的節(jié)點(diǎn)坛梁,同時(shí)也可用于恢復(fù)任務(wù)
…
}
```
Fiber和fiber不是同一個(gè)概念而姐。前者代表新的調(diào)和器,后者代表fiber node划咐,也可以認(rèn)為是改進(jìn)后的虛擬DOM
(2)調(diào)度器
每次有新的更新任務(wù)發(fā)生的時(shí)候拴念,調(diào)度器都會(huì)按照策略給這些任務(wù)分配一個(gè)優(yōu)先級(jí)。通過(guò)這個(gè)優(yōu)先級(jí)可以獲取一個(gè)該更新任務(wù)必須執(zhí)行的截止時(shí)間褐缠,優(yōu)先級(jí)越高截止時(shí)間就越近政鼠,反之亦然。調(diào)度器通過(guò)實(shí)現(xiàn)requestIdleCallback函數(shù)來(lái)做到在瀏覽器空閑的時(shí)候去執(zhí)行這些更新任務(wù)队魏。簡(jiǎn)單的說(shuō)缔俄,就是通過(guò)定時(shí)器的方式來(lái)獲取每一幀的結(jié)束時(shí)間,得到每一幀的結(jié)束時(shí)間就能判斷當(dāng)下距離結(jié)束時(shí)間的差值。如果還未到結(jié)束時(shí)間俐载,則可以繼續(xù)執(zhí)行更新任務(wù)蟹略,如果已經(jīng)過(guò)了結(jié)束時(shí)間,那么當(dāng)前幀已經(jīng)沒(méi)有時(shí)間執(zhí)行任務(wù)遏佣,必須把執(zhí)行權(quán)交還給瀏覽器锋谐,即打斷任務(wù)的執(zhí)行怒竿。另外當(dāng)開始執(zhí)行更新任務(wù)時(shí),如果有新的更新任務(wù)進(jìn)來(lái),那么調(diào)度器就會(huì)按照兩者的優(yōu)先級(jí)大小來(lái)進(jìn)行決策十电。如果新的任務(wù)優(yōu)先級(jí)小急前,那么繼續(xù)當(dāng)下的任務(wù)鹃祖,如果新的任務(wù)優(yōu)先級(jí)大躏吊,那么會(huì)打斷任務(wù)并開始新的任務(wù)