被嚴(yán)重低估!React 19 又是一次開發(fā)方式的變革,useEffect 將會(huì)逐漸退出歷史舞臺(tái)
目前寫 React 19 的文章已經(jīng)有很多很多了,但是,寫到點(diǎn)子上的并不多。包括官方文檔,只是簡單的羅列出了它新增了一些 hook,一些特性,卻并沒有進(jìn)一步說明這些 hook 背后所代表的含義,它們的最佳實(shí)踐是什么。
以至于,在這個(gè)時(shí)間節(jié)點(diǎn),期待 React 19 的人也并不是很多。
但是,我要告訴大家的是,我們都嚴(yán)重低估了 React 19。
beta 版在 npm 上可用之后,我就創(chuàng)建了一個(gè)項(xiàng)目,把 React 新增的特性都使用了一遍。如下圖所示。
我用新的開發(fā)方式,將我們?cè)陧?xiàng)目開發(fā)中可能會(huì)遇到的情況都實(shí)現(xiàn)了一遍。寫完十幾個(gè)案例之后,我的感受就是:
我們的開發(fā)方式,又要迎來一次重大升級(jí)了。因?yàn)樾碌?hook 真的太好用了。
和過去的 React 版本相比,React 19 主要從如下兩個(gè)方面帶來了顯著的提升。
一、性能
最近幾年,許多其他前端框架紛紛擁抱基于 Signal 的細(xì)粒度更新,在特定場(chǎng)景有接近原生開發(fā)的性能體驗(yàn),把前端框架的性能問題推上了風(fēng)口浪尖。而 React 基于 Fiber 的 Diff 更新則自然而然的成為了最大的缺陷與短板。大量冗余 re-render 造成的性能損耗,是 React 不得不面對(duì)的挑戰(zhàn)。
注:細(xì)粒度更新并非所有場(chǎng)景都具有明顯的優(yōu)勢(shì),也不要完全相信框架而忽視掌握性能優(yōu)化技能的重要性。
雖然 React 提供了 memo/useMemo/useCallback 來幫助開發(fā)者優(yōu)化項(xiàng)目性能。但是他們的學(xué)習(xí)成本并不低,想要在項(xiàng)目中得心應(yīng)手的使用他們更是不易。這就造成了許多團(tuán)隊(duì)在沒有完全消化他們的情況下,對(duì)他們的使用存在濫用的情況。
在這樣的背景之下,React 19 將會(huì)推出 React Compiler,在開發(fā)者不調(diào)整任何代碼的情況下,自動(dòng)優(yōu)化項(xiàng)目性能。
React Compiler 能夠幫助我們?cè)诓皇褂?nbsp;memo/useMemo/useCallback 的情況下,方便的處理好項(xiàng)目 re-render 的問題。你的項(xiàng)目最終只會(huì)在需要更新的地方 re-render。
并且最重要的是,React Compiler 編譯之后,你的代碼并不會(huì)改變現(xiàn)有渲染機(jī)制,而是在已有機(jī)制下完成對(duì)項(xiàng)目的優(yōu)化。React Compiler 的愿景非常龐大,他需要在兼顧大量老項(xiàng)目的情況下,做到對(duì)項(xiàng)目的性能優(yōu)化。因此與提出一個(gè)新的解決方案相比,他的開發(fā)難度要高出很多。破壞性更新顯得更加容易,但是對(duì)于開發(fā)者和大量老項(xiàng)目而言,這是一種嚴(yán)重的傷害。React Compiler 則選擇了最難搞的一種更新方式。
與依賴追蹤的細(xì)粒度更新不同,React Compiler 通過記憶的方式,讓組件更新只發(fā)生在需要更新的組件,從而減少大量 re-render 的組件,我會(huì)在后續(xù)的章節(jié)中詳細(xì)介紹他的使用原理。
但是請(qǐng)注意,React Compiler 并非全能,如果你寫的代碼過于靈活,無法被提前預(yù)判執(zhí)行行為,那么 React Compiler 將會(huì)跳過這一部分的優(yōu)化。因此好的方式是在項(xiàng)目中引入嚴(yán)格模式,在嚴(yán)格模式的指導(dǎo)下完成的開發(fā),基本都在 React Compiler 的輻射范圍之內(nèi)。
二、開發(fā)體驗(yàn)
與性能帶來的提升相比,真正令我非常期待的是,React 19 將會(huì)迎來一次開發(fā)體驗(yàn)的重大提升。畢竟沒有 React Compiler,我自己也能優(yōu)化好我的項(xiàng)目性能。
開發(fā)體驗(yàn)的提升主要體現(xiàn)在,React 19 之后,我們可能不再需要 useEffect 了。
useEffect 是一個(gè)功能強(qiáng)大的 hook,但他又是最難駕馭的一個(gè) hook,理解不夠的開發(fā)者可能會(huì)由于濫用它而導(dǎo)致項(xiàng)目失控。包括被討論最多的閉包問題,也往往跟它有關(guān)。其中最考驗(yàn)開發(fā)者水平的,是對(duì)于 useEffect 依賴項(xiàng)的正確處理。
React19 的 大部分更新,幾乎都是圍繞如何在開發(fā)中盡量不用或者少用 useEffect 來展開。在之前的項(xiàng)目開發(fā)中,useEffect 是我們處理異步問題必須使用的重要 hook 之一,他幾乎存在于每一個(gè)頁面組件之中。
React 19 則引入了新的 hook,讓我們處理異步開發(fā)時(shí),不再需要 useEffect,從而極大的改變我們的開發(fā)方式。我會(huì)在后續(xù)的章節(jié)中,結(jié)合大量實(shí)踐案例,一一介紹這些 API 的詳細(xì)使用方法,確保每個(gè)讀者都能徹底掌握他。
除此之外,React19 想要徹底改變我們?cè)陧?xiàng)目開發(fā)中的 UI 交互方式。因此對(duì)于 React19 而言,Suspense、ErrorBoundary、Action 的重要性將會(huì)變得越來越高。
Suspense、ErrorBoundary 雖然早在 React18 中都能夠被正常使用,但是由于異步請(qǐng)求方案的不成熟,它們并沒有被普及開,包括 React 官方文檔也并沒有進(jìn)一步說明如何觸發(fā) Suspense 的回調(diào)機(jī)制。因此它們只是小范圍的被一些頂尖的前端開發(fā)所使用。
<Suspense fallback={<div>loading...</div>}>
<List api={__api} />
</Suspense>
React19 之后,它們將會(huì)得到普及。這將會(huì)進(jìn)一步深化組件解釋一切的開發(fā)思想。
三、React Server Components
React Server Components 是 React 在探索前端邊界的又一次突破性的創(chuàng)舉。它是一種新概念組件。我們可以在構(gòu)建時(shí)運(yùn)行一次組件,以提高頁面的渲染速度。
預(yù)渲染、增量渲染、流式傳輸?shù)雀拍顚?duì)提高大型復(fù)雜項(xiàng)目的用戶體驗(yàn)有非常大的幫助。好消息是,RSC 已經(jīng)在 Next.js 中得到落地實(shí)踐。
目前已經(jīng)有大量的開發(fā)者在使用 Next.js。我們會(huì)在后續(xù)的章節(jié)中詳細(xì)給大家介紹 RSC。
四、新的架構(gòu)思維
React 19 之前,React 高手與普通開發(fā)者之間,開發(fā)的項(xiàng)目無論是從性能上、還是從代碼優(yōu)雅上差距都非常大。因此不同的人對(duì)于 React 的感受完全不一樣。
而 React 19 則借由推出一些新的 hook,暗中傳遞一種框架思維「最佳實(shí)踐」,這將會(huì)極大的拉進(jìn)普通開發(fā)者與頂尖高手之間的差距。對(duì)于大多數(shù) React 開發(fā)者而言,這會(huì)是一個(gè)極大的提升。
這一意圖在 React 新的官方文檔與 Next.js 中提現(xiàn)得非常明顯。
這一最佳實(shí)踐主要圍繞如何改進(jìn)異步編程的開發(fā)體驗(yàn)而展開。在后續(xù)的章節(jié)中大家可以自行感受。我也會(huì)在后續(xù)的實(shí)踐案例中弱化對(duì) useEffect 的使用。
例如,當(dāng)我想要實(shí)現(xiàn)如下效果時(shí)。
在項(xiàng)目開發(fā)中,新頁面渲染時(shí)請(qǐng)求一個(gè)接口的場(chǎng)景非常常見。新的架構(gòu)思維的開發(fā)代碼如下所示。
該案例沒有引入任何三方庫。
首先我們需要定義一個(gè) API 用于請(qǐng)求數(shù)據(jù)。
const api = async () => {
const res = await fetch('https://api.chucknorris.io/jokes/random')
return res.json()
}
然后創(chuàng)建一個(gè)函數(shù)組件,并執(zhí)行該 api。
export default function Index() {
const __api = api()
return (
<div>
<div id='tips'>初始化時(shí),自動(dòng)獲取一條數(shù)據(jù)內(nèi)容</div>
<Suspense fallback={<div>loading...</div>}>
<Item api={__api} />
</Suspense>
</div>
)
}
最后在子組件中,獲取 api 執(zhí)行之后得到的數(shù)據(jù)。
const Item = (props) => {
const joke = use(props.api)
return (
<div>
<div>{joke.value}</div>
</div>
)
}
大家可以自行感受一下新的開發(fā)方式與以前基于 useEffect 請(qǐng)求數(shù)據(jù)有什么不同之處。
注意:一套成熟架構(gòu)思維,不是使用一個(gè)簡單的方案解決某一個(gè)問題,而是要基于這套思維去解決項(xiàng)目中的絕大多數(shù)場(chǎng)景。因此我們一定要結(jié)合大量的場(chǎng)景去理解一套項(xiàng)目架構(gòu)思維。