新聞中心
大家好,我是卡頌。

今天和同事聊天,我說他是個鐵憨憨,不會和女生聊天。
他啪的一下跳起來,“我可懂情調(diào)了”
“哦?那你來句土味情話?!?/p>
他清清嗓子,壓低了腔調(diào),望向遠(yuǎn)方,緩緩道:
- 如果我是component,我對你的情愫在didMount時燃起,直到我生命unmount時熄滅
正當(dāng)他沉浸在YY的世界無法自拔時,我說:
- 你知道在React18,componentDidMount和componentWillUnmount可能調(diào)用多次么?
從Strict Mode談起
React有個特性 — Strict Mode,被StrictMode包裹的組件在DEV環(huán)境會對不推薦寫法有更嚴(yán)格的提示與輔助檢測行為。
「輔助檢測行為」是指部分方法會被React重復(fù)調(diào)用,幫助開發(fā)者更容易發(fā)現(xiàn)不規(guī)范使用這些方法時的潛在bug。
所有會被重復(fù)調(diào)用的API見StrictMode文檔[1]
舉個例子:
- function App() {
- const [num, update] = useState(0);
- function onClick() {
- update(num + 1);
- }
- console.log('render');
- return (
{num}
- );
- }
當(dāng)App被StrictMode包裹,點擊p觸發(fā)更新后,App組件會render兩次。
- 在v17之前,例子中console.log會執(zhí)行兩次。但在v17之后,React覆寫了console方法,所以console.log只會執(zhí)行一次,但組件實際會render兩次
這么做的目的是:作為函數(shù)組件,App的「副作用」應(yīng)該在useEffect回調(diào)中執(zhí)行。
如果不規(guī)范書寫副作用(比如在組件函數(shù)體內(nèi)寫副作用),那么重復(fù)render更容易暴露可能產(chǎn)生的bug。
鋪墊完背景。接下來,讓我們揭露React善變的渣男行徑。
最近刷v18討論組時突然發(fā)現(xiàn):StrictMode中會增加一條Strict Effect規(guī)則。
Strict Effect
簡單的說,類似上文講到的部分API在StrictMode下會重復(fù)執(zhí)行。
Strict Effect規(guī)則會讓useEffect、useLayoutEffect在StrictMode下也會重復(fù)執(zhí)行。
比如:
- function App() {
- // 或useLayoutEffect
- useEffect(() => {
- // 邏輯1
- return () => // 邏輯2;
- }, [])
- // ...
- }
在當(dāng)前React中,組件mount時,執(zhí)行邏輯1。
而在Strict Effect規(guī)則下,mount時的邏輯如下:
- 組件mount時,執(zhí)行邏輯1
-
React模擬組件unmount,執(zhí)行邏輯2
-
React模擬組件mount,執(zhí)行邏輯1
注意,這里useEffect的依賴項是[],在以往的認(rèn)知里,依賴項為「空數(shù)組」意味著該useEffect邏輯只會在mount時執(zhí)行一次。
而在v18的Strict Mode,由于包含了Strict Effect規(guī)則,mount時的useEffect邏輯會被重復(fù)執(zhí)行。
某種程度上講,這種打破開發(fā)者既有認(rèn)知的Breaking Change,比Concurrent Mode更讓人難以接受。
那么React團(tuán)隊為什么要設(shè)計這條規(guī)則呢?
一切為了Offscreen
Offscreen是一個開發(fā)中的API,預(yù)計會在某個v18的小版本發(fā)布。
他的功能類似Vue中的keep-alive,用來在組件「失活」時在后臺保存組件狀態(tài)。
舉個Tab切換的例子,在Posts和Archive之間切換Tab:
當(dāng)切換到Posts時,Archive屬于「失活」?fàn)顟B(tài)。
如果不需要保存狀態(tài),則銷毀Archive組件。當(dāng)切換到Archive Tab時,再重新mount Archive。
當(dāng)需要保存狀態(tài)時,只能將Posts與Archive的狀態(tài)保存在他們的父組件或狀態(tài)管理(比如Redux)中。
而有了Offscreen API,在Fiber樹(可以理解為虛擬DOM樹)層面,可以保存失活的組件結(jié)構(gòu)與狀態(tài)。
這個API的應(yīng)用場景主要包括:
- 切換路由時保存之前路由的狀態(tài)
- 預(yù)加載將要切換的路由
現(xiàn)在問題來了:當(dāng)Offscreen組件從「失活」變?yōu)椤富顒印?,會觸發(fā)什么生命周期函數(shù)呢?
答案是:componentDidMount以及:
- useEffect(() => {
- // 觸發(fā)這個邏輯...
- }, [])
當(dāng)Offscreen組件從「活動」變?yōu)椤甘Щ睢箷r,會觸發(fā)componentWillUnmount與:
- useEffect(() => {
- // ...
- return () => {
- // 觸發(fā)這個邏輯...
- }
- }, [])
所以,這些曾經(jīng)被認(rèn)為在組件生命周期中只會觸發(fā)一次的方法,由于Offscreen,在未來可能會多次觸發(fā)。
這也是React提前在StrictMode中加上Strict Effect規(guī)則的原因。
就像渣男變心前都會有些反常的舉動。
React18是真的挑戰(zhàn)
不管是Offscreen還是Concurrent Mode,可以預(yù)見隨著v18的到來,React會更強(qiáng)大,相應(yīng)的學(xué)習(xí)曲線會更陡峭。
這既是機(jī)遇,也是挑戰(zhàn)。
千萬別等變化一股腦到眼前時再埋怨:
你個渣男,當(dāng)初說好一心一意只會觸發(fā)一次,現(xiàn)在為了妖艷新特性,背叛我們的諾言。
到那時React只會拍拍屁股轉(zhuǎn)身,留下不羈的背影:
參考資料
[1]StrictMode文檔:
https://zh-hans.reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects
當(dāng)前文章:React要更新,就像渣男會變心
標(biāo)題網(wǎng)址:http://www.5511xx.com/article/djisihd.html


咨詢
建站咨詢
