隨著 React 17 的發(fā)布,我們會(huì)驚訝地發(fā)現(xiàn),v17 版本的最大特性是無新特性
征讲。因?yàn)樗鼪]有添加任何面向開發(fā)人員的新功能,而是專注在了如何簡(jiǎn)化 React 本身的升級(jí)录择。
React 17 可以說是發(fā)揮了「跳板」的作用拔莱,它將使由一個(gè)版本的 React 管理的樹嵌入到由另一個(gè)版本的 React 管理的樹中更加安全碗降。也就是說在不遠(yuǎn)的將來,使用 React 17 及以上的項(xiàng)目在升級(jí)的時(shí)候可以考慮部分模塊仍然使用 V17 的版本塘秦。
那讓我們來一起看看 V17 版本都做了些什么讼渊?
漸進(jìn)式升級(jí)
React 17 支持逐步的 React 升級(jí)。
在 React 17 之前尊剔,React 升級(jí)必須整個(gè)應(yīng)用升級(jí)到指定版本爪幻,不能一個(gè)應(yīng)用中存在兩個(gè) React 版本。但是须误,如果代碼比較陳舊挨稿,并且沒有得到很好的維護(hù),則升級(jí)的挑戰(zhàn)會(huì)更大京痢。
可能有人會(huì)問奶甘,為什么我們不可以在頁面上使用兩個(gè) React 版本?
因?yàn)樵陧撁嫔鲜褂脙蓚€(gè)版本的 React 祭椰,可能會(huì)導(dǎo)致事件問題臭家。在下面的 事件變更
中會(huì)詳細(xì)講到疲陕。
當(dāng) React 17 發(fā)布之后,在下一個(gè)版本問世時(shí)钉赁,帶來了更多的選擇蹄殃。我們可以不必像以前一樣,可以選擇逐個(gè)升級(jí)應(yīng)用程序你踩。例如诅岩,您可以決定將大部分應(yīng)用程序遷移到 React 18,但在 React 17 上仍然保留一些不常變動(dòng)的頁面或子路由带膜。
但是按厘,這不意味著您必須逐步升級(jí)。
對(duì)于大多數(shù)應(yīng)用程序钱慢,全部升級(jí)仍然是最好的解決方案逮京。但是,對(duì)于沒有積極維護(hù)的大型應(yīng)用程序束莫,可以考慮使用逐步升級(jí)懒棉,并且新版本的 React 也可以使這些應(yīng)用不落伍。
React 若想逐步升級(jí)览绿,就應(yīng)該啟用漸進(jìn)式的更新策严,所以需要對(duì) React 事件系統(tǒng)進(jìn)行一些更改。
事件變更
React 17 將事件處理附加到 rootNode 上饿敲,而不是 document 上妻导。
由上圖可以發(fā)現(xiàn),在 React 16 及以前怀各,React 對(duì)事件執(zhí)行 document.addEventListener()倔韭,而在 React 17 以后,React 對(duì)根 DOM 執(zhí)行 rootNode.addEventListener()瓢对。
也就是說寿酌,在 React 17 中,React 將不再在 document 級(jí)別 attach 事件處理器硕蛹,而是 attach 到 React 渲染樹的根 DOM 容器中:
const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);
這么做的原因是什么呢醇疼?
在 React 17 以前,所有的事件會(huì)被附加到 document 上法焰,并為它建一個(gè)處理器秧荆,當(dāng) DOM 事件觸發(fā)時(shí),會(huì)向上冒泡埃仪,一直到 document 級(jí)別乙濒,也就是附加事件處理器的地方,事件會(huì)得到響應(yīng)贵试。
如果頁面上有多個(gè) React 版本琉兜,事件都會(huì)被附加在 document 上凯正。這時(shí)嵌套的 React 樹調(diào)用 e.stopPropagation() 停止了事件冒泡,外部的樹仍會(huì)接收到該事件豌蟋,這就使嵌套不同版本的 React 難以實(shí)現(xiàn)廊散。
React DOM 變更
禁止在 onScroll 事件時(shí)冒泡。
React onFocus 和 onBlur 事件已轉(zhuǎn)換為瀏覽器下的原生 focusin 和 focusout 事件梧疲,這與 React 的現(xiàn)有實(shí)現(xiàn)更為接近允睹,有時(shí)還能提供額外的信息。
將所有 Capture 事件都使用瀏覽器的捕獲階段實(shí)現(xiàn)幌氮。
異步運(yùn)行 useEffect 清理函數(shù)缭受。
useEffect(() => {
return () => {
// 清理函數(shù)
};
});
在 React 16 中,effect 的清理函數(shù)會(huì)同步運(yùn)行该互,在大型應(yīng)用中大量的數(shù)據(jù)變更會(huì)減慢屏幕變換的速度米者,而且,大多數(shù) effect 都不需要延緩屏幕的更新宇智。
在 React 17 中蔓搞, effect 清理函數(shù)是異步運(yùn)行的。并且随橘,React 17 會(huì)根據(jù) effect 在樹中的位置喂分,以相同的順序執(zhí)行清理函數(shù)。以前机蔗,這個(gè)順序會(huì)有所不同蒲祈。
例如,如果要卸載組件萝嘁,清理函數(shù)將在屏幕更新后運(yùn)行梆掸。
如果希望同步執(zhí)行,這時(shí)你可以使用 useLayoutEffect酿愧。
- 移除事件池沥潭。
function onChange(e) {
setData(data => ({
...data,
text: e.target.value
}));
}
在 React 16 及更早版本中,React 為了提高性能復(fù)用了不同事件之間的事件對(duì)象嬉挡,并將所有事件字段高為 null。在上面代碼中若想正確使用汇恤,須得調(diào)用 e.persist()庞钢。
而在 React 17 中,上面代碼可以好好地運(yùn)行因谎。
- 如果 forwardRef 或 memo 組件的返回值為 undefined基括,則拋出異常。
const Button = forwardRef(() => {
<button />;
});
const Button = memo(() => {
<button />;
});
上面代碼中财岔,React 17 會(huì)把它標(biāo)記為 error 而非忽略它风皿。
在 React 16 及更早版本中河爹,React 僅會(huì)對(duì)類和函數(shù)組件返回 undefined 時(shí)會(huì)當(dāng)成錯(cuò)誤,但不檢查 forwardRef 和 memo 組件的返回值桐款。
而在 React 17 中咸这,對(duì) forwardRef 和 memo 組件的處理與普通函數(shù)和類組件一致,即在它們返回 undefined 會(huì)被視為錯(cuò)誤魔眨。
移除 React Native Web 不需要的內(nèi)部組件媳维。
棄用未記錄且具有誤導(dǎo)性的 ReactTestUtils.SimulateNative API。
修復(fù)當(dāng) dangerouslySetInnerHTML 為 undefined 時(shí)遏暴,誤報(bào)警告的問題侄刽。
React DOM Server
使用服務(wù)端渲染的 useCallback 與 useMemo 一致。
修復(fù)函數(shù)組件拋出異常時(shí)狀態(tài)泄露的問題朋凉。