React 團(tuán)隊(duì)最近在忙啥?
距離 React 最近一次版本更新已經(jīng)過(guò)去了 8 個(gè)多月。那最近 React 都在忙些啥呢?下面就來(lái)看看 React 團(tuán)隊(duì)最近正在研究的事,以及取得的進(jìn)展!
概覽:
- React Server Components
- 資源加載
- 文檔元數(shù)據(jù)
- 離屏渲染
- Transition Tracing
React Server Components
React Server Components(RSC,服務(wù)端組件)是由 React 團(tuán)隊(duì)設(shè)計(jì)的一種新的應(yīng)用架構(gòu)。
React 團(tuán)隊(duì)正在引入一種新的組件——服務(wù)器組件,它提前運(yùn)行并且被排除在 JavaScript 包之外。服務(wù)器組件可以在構(gòu)建期間運(yùn)行,從文件系統(tǒng)讀取或獲取靜態(tài)內(nèi)容。它們還可以在服務(wù)端運(yùn)行,無(wú)需構(gòu)建 API 即可訪問(wèn)數(shù)據(jù)層??梢酝ㄟ^(guò) props 將數(shù)據(jù)從服務(wù)端組件傳遞到瀏覽器中的交互式客戶端組件。
RSC 將以服務(wù)端為中心的多頁(yè)面應(yīng)用的簡(jiǎn)單“請(qǐng)求/響應(yīng)”心智模型與以客戶端為中心的單頁(yè)應(yīng)用的無(wú)縫交互相結(jié)合,提供了兩全其美的體驗(yàn)。
自上次更新以來(lái),React 團(tuán)隊(duì)合并了 React 服務(wù)端組件 RFC 以批準(zhǔn)該提案。通過(guò) [React Server Module Conventions](React Server Module Conventions) 提案解決了懸而未決的問(wèn)題,并與合作伙伴達(dá)成共識(shí)以遵循“use client”的約定。這些文檔還充當(dāng) RSC 兼容實(shí)現(xiàn)應(yīng)支持的規(guī)范。
最大的變化就是引入了 async / await? 作為從服務(wù)端組件獲取數(shù)據(jù)的主要方式。 除此之外,還計(jì)劃通過(guò)引入一個(gè)名為 ??use?
?? 的新 Hook 來(lái)支持從客戶端加載數(shù)據(jù),該 Hook 用于解包 Promises。雖然不能在僅客戶端應(yīng)用的任意組件中支持 ??async / await?
?,但計(jì)劃在構(gòu)建僅客戶端應(yīng)用時(shí)添加對(duì)它的支持,類似于 RSC 應(yīng)用的結(jié)構(gòu)。
現(xiàn)在已經(jīng)解決了數(shù)據(jù)獲取的問(wèn)題,并正在探索另一個(gè)方向:將數(shù)據(jù)從客戶端發(fā)送到服務(wù)器,以便可以執(zhí)行數(shù)據(jù)庫(kù)突變和實(shí)現(xiàn)表單。通過(guò)讓跨服務(wù)端/客戶端邊界傳遞服務(wù)端操作(Server Action)函數(shù)來(lái)實(shí)現(xiàn)這一點(diǎn),然后客戶端可以調(diào)用這些函數(shù),從而提供無(wú)縫的 RPC。Server Actions 還在 JavaScript 加載之前提供逐步增強(qiáng)的表單。
React 服務(wù)端組件已經(jīng)在 Next.js 應(yīng)用路由中發(fā)布。這展示了一個(gè)真正將 RSC 視為原語(yǔ)的路由的深度集成,但這并不是構(gòu)建與 RSC 兼容的路由和框架的唯一方法。RSC 規(guī)范和實(shí)現(xiàn)提供的特性有明顯的區(qū)別。React 服務(wù)端組件是指跨兼容React框架工作的組件規(guī)范。
React 團(tuán)隊(duì)通常建議使用現(xiàn)有框架,但如果需要構(gòu)建自己的自定義框架,也是可以的。構(gòu)建自己的 RSC 兼容框架并不容易,因?yàn)樾枰疃葮?gòu)建工具集成。當(dāng)前一代的構(gòu)建工具非常適合在客戶端上使用,但它們?cè)谠O(shè)計(jì)時(shí)并沒(méi)有為在服務(wù)端和客戶端之間拆分單個(gè)模塊圖提供一流的支持。這就是為什么 React 團(tuán)隊(duì)現(xiàn)在直接與構(gòu)建工具開(kāi)發(fā)人員合作以獲得內(nèi)置的 RSC。
資源加載
Suspense 允許指定在組件的數(shù)據(jù)或代碼仍在加載時(shí)在屏幕上顯示什么內(nèi)容。這使用戶可以在頁(yè)面加載時(shí)以及加載更多數(shù)據(jù)和代碼的路由導(dǎo)航期間逐步看到更多內(nèi)容。但是,從用戶的角度來(lái)看,在考慮新內(nèi)容是否準(zhǔn)備就緒時(shí),數(shù)據(jù)加載和渲染并不能說(shuō)明全部。默認(rèn)情況下,瀏覽器獨(dú)立加載樣式表、字體和圖像,這可能導(dǎo)致 UI 跳轉(zhuǎn)和連續(xù)的布局轉(zhuǎn)換。
React 團(tuán)隊(duì)正在努力將 Suspense 與樣式表、字體和圖像的加載生命周期完全集成,以便 React 將它們考慮在內(nèi),以確定內(nèi)容是否已準(zhǔn)備好顯示。在不改變編寫 React 組件的方式的情況下,更新將以更加連貫的方式進(jìn)行。作為一種優(yōu)化。,還將提供一種手動(dòng)方式來(lái)直接從組件中預(yù)加載字體等資源。
文檔元數(shù)據(jù)
應(yīng)用中的不同頁(yè)面可能具有不同的元數(shù)據(jù),例如 <title> 標(biāo)簽、description 和其他特定于此頁(yè)面的 <meta> 標(biāo)簽。從維護(hù)的角度來(lái)看,將此信息放在該頁(yè)面的 React 組件中會(huì)更具可擴(kuò)展性。但是,此元數(shù)據(jù)的 HTML 標(biāo)簽需要位于文檔的 <head> 中,該文檔通常在應(yīng)用的最根組件中。
現(xiàn)在有兩種方法可以解決這個(gè)問(wèn)題:
- 渲染一個(gè)特殊的第三方組件,該組件將 <title>、<meta> 和其中的其他標(biāo)簽移動(dòng)到文檔的 <head> 中。這適用于主流瀏覽器,但有許多客戶端不運(yùn)行客戶端 JavaScript,例如 Open Graph 解析器,因此該技術(shù)并不普遍適用。
- 將頁(yè)面分為兩部分進(jìn)行服務(wù)端渲染。首先,渲染主要內(nèi)容并收集所有此類標(biāo)簽。然后使用這些標(biāo)簽渲染 <head> 。最后,<head> 和主要內(nèi)容被發(fā)送到瀏覽端。這種方法是可行的,但這樣就不能使用 React 18 中的流式服務(wù)端渲染了,因?yàn)楸仨毜却袃?nèi)容渲染完畢才能發(fā)送 <head>。
這就是為什么 React 要 在組件樹(shù)中的任何位置添加內(nèi)置支持來(lái)渲染 <title>、<meta>? 和元數(shù)據(jù) <link> 標(biāo)簽。它在所有環(huán)境中都以相同的方式工作,包括完全的客戶端代碼、SSR,以及未來(lái)的 RSC。
React 優(yōu)化編譯器
?React 團(tuán)隊(duì)一直在積極迭代 React Forget 的設(shè)計(jì),這是一個(gè)針對(duì) React 的優(yōu)化編譯器。之前曾將其稱為“自動(dòng)記憶編譯器”,這在某種意義上是正確的。但是構(gòu)建編譯器幫助 React 團(tuán)隊(duì)更深入地理解了 React 的編程模型。理解 React Forget 的更好方法是將其作為一個(gè)自動(dòng)響應(yīng)式編譯器。
React 的核心思想是開(kāi)發(fā)人員將 UI 定義為當(dāng)前狀態(tài)的函數(shù)。使用普通的 JavaScript 值:數(shù)字、字符串、數(shù)組、對(duì)象,并使用標(biāo)準(zhǔn)的 JavaScript 語(yǔ)法:if/else、for 等描述組件邏輯。心智模型是 React 將在應(yīng)用狀態(tài)更改時(shí)重新渲染。這種簡(jiǎn)單的心智模型和與 JavaScript 語(yǔ)義保持接近是 React 編程模型中的一個(gè)重要原則。
問(wèn)題是 React 有時(shí)會(huì)響應(yīng)過(guò)度:它會(huì)重新渲染太多。例如,在 JavaScript 中沒(méi)有直接的方法來(lái)比較兩個(gè)對(duì)象或數(shù)組是否相等(具有相同的鍵和值),因此在每次渲染時(shí)創(chuàng)建一個(gè)新的對(duì)象或數(shù)組可能會(huì)導(dǎo)致 React 執(zhí)行比它嚴(yán)格需要的更多的工作。這意味著開(kāi)發(fā)人員必須明確記憶組件,以免對(duì)更改響應(yīng)過(guò)度。
React Forget 的目標(biāo)是確保 React 應(yīng)用在默認(rèn)情況下具有適量的響應(yīng):應(yīng)用僅在狀態(tài)值發(fā)生有意義的變化時(shí)才重新渲染。從實(shí)現(xiàn)的角度來(lái)看,這意味著自動(dòng)記憶,但 React 團(tuán)隊(duì)認(rèn)為響應(yīng)式框架是理解 React 和 Forget 的更好方式。React 目前會(huì)在對(duì)象標(biāo)識(shí)更改時(shí)重新渲染。有了 Forget,React 會(huì)在語(yǔ)義值發(fā)生變化時(shí)才重新渲染——但不會(huì)產(chǎn)生深度比較的運(yùn)行時(shí)成本。
進(jìn)展而言,自上次更新以來(lái),React 團(tuán)隊(duì)對(duì)編譯器的設(shè)計(jì)進(jìn)行了大量迭代,以與這種自動(dòng)響應(yīng)式方法保持一致,并納入內(nèi)部使用編譯器的反饋。在去年年底開(kāi)始對(duì)編譯器進(jìn)行一些重大重構(gòu)之后,現(xiàn)在已經(jīng)開(kāi)始在 Meta 部分生產(chǎn)中使用編譯器。一旦在生產(chǎn)中證明了它的價(jià)值,就計(jì)劃將其開(kāi)源。?
離屏渲染
離屏渲染是 React 即將推出的一項(xiàng)功能,用于在后臺(tái)渲染屏幕而無(wú)需額外的性能開(kāi)銷??梢詫⑵湟暈?CSS 屬性 ??content-visibility?
? 的一個(gè)版本,它不僅適用于 DOM 元素,也適用于 React 組件。在研究過(guò)程中,發(fā)現(xiàn)了各種用例:
- 路由可以在后臺(tái)預(yù)渲染頁(yè)面,以便當(dāng)用戶導(dǎo)航到該頁(yè)面時(shí),頁(yè)面立即可用;
- 選項(xiàng)卡切換組件可以保留隱藏選項(xiàng)卡的狀態(tài),因此用戶可以在它們之間切換而不會(huì)丟失進(jìn)度。
- 虛擬列表組件可以在可見(jiàn)窗口上方和下方預(yù)渲染額外的行。
- 打開(kāi)模態(tài)框或彈出窗口時(shí),可以將應(yīng)用的其余部分置于“后臺(tái)”模式,以便除模式之外的所有內(nèi)容都禁用事件和更新。
大多數(shù) React 開(kāi)發(fā)人員不會(huì)直接與 React 屏幕外的 API 交互。相反,離屏渲染將被集成到路由和 UI 庫(kù)中,使用這些庫(kù)的開(kāi)發(fā)人員將自動(dòng)受益,而無(wú)需額外的工作。
這個(gè)功能可以讓我們?cè)诓桓淖兙帉懡M件的方式的情況下在屏幕外渲染任何 React 樹(shù)。當(dāng)一個(gè)組件在屏幕外渲染時(shí),它實(shí)際上并沒(méi)有掛載,直到組件可見(jiàn)——它的 effect 不會(huì)被觸發(fā)。離屏渲染的一個(gè)關(guān)鍵特性就是可以在不丟失其狀態(tài)的情況下切換組件的可見(jiàn)性。
自上次更新以來(lái),React 團(tuán)隊(duì)在 Android 和 iOS 上的 React Native 應(yīng)用中測(cè)試了 Meta 內(nèi)部預(yù)渲染的實(shí)驗(yàn)版本,并取得了積極的性能結(jié)果。除此之外,還改進(jìn)了離屏渲染與 Suspense 的工作方式——在離屏樹(shù)內(nèi)暫停不會(huì)觸發(fā) Suspense 回退。剩下的工作就涉及完成向庫(kù)開(kāi)發(fā)人員公開(kāi)的原語(yǔ)。希望在今年晚些時(shí)候發(fā)布一個(gè) RFC,以及一個(gè)用于測(cè)試和反饋的實(shí)驗(yàn)性 API。
Transition Tracing
Transition Tracing API 可以檢測(cè) React Transitions 何時(shí)變慢并調(diào)查它們變慢的原因。在上次更新后,React 團(tuán)隊(duì)完成了 API 的初始設(shè)計(jì)并發(fā)布了 RFC,基本能力也已經(jīng)實(shí)現(xiàn),該項(xiàng)目目前處于擱置狀態(tài)。歡迎對(duì) RFC 進(jìn)行反饋,并期待恢復(fù)其開(kāi)發(fā),為 React 提供更好的性能測(cè)量工具。這對(duì)于構(gòu)建在基于 React Transitions 的路由特別有用,比如 Next.js 應(yīng)用路由。
參考:https://react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023