React Compiler 終于開(kāi)源了。
自從從它第一次在 React Conf 2021 亮相。到現(xiàn)在 React Conf 2024 正式開(kāi)源,我已經(jīng)苦等了三年之久。盼星星盼月亮,終于把他給盼來(lái)了。
i
以前叫 React Forget,現(xiàn)改名為 React Compiler。
要了解 React Compiler,這還需要從 React 的更新機(jī)制說(shuō)起。React 項(xiàng)目中的任何一個(gè)組件發(fā)生 state 狀態(tài)的變更,React 更新機(jī)制都會(huì)從最頂層的根節(jié)點(diǎn)開(kāi)始往下遞歸對(duì)比,通過(guò)雙緩存機(jī)制判斷出哪些節(jié)點(diǎn)發(fā)生了變化,然后更新節(jié)點(diǎn)。這樣的更新機(jī)制成本并不小,因?yàn)樵谂袛噙^(guò)程中,如果 React 發(fā)現(xiàn) props、state、context 任意一個(gè)不同,那么就認(rèn)為該節(jié)點(diǎn)被更新了。因此,冗余的 re-render 在這個(gè)過(guò)程中會(huì)大量發(fā)生。
?
對(duì)比的成本非常小,但是 re-render 的成本偏高,當(dāng)我們?cè)诙虝r(shí)間之內(nèi)快速更改 state 時(shí),程序大概率會(huì)存在性能問(wèn)題。因此在以往的開(kāi)發(fā)方式中,掌握性能優(yōu)化的手段是高級(jí) React 開(kāi)發(fā)者的必備能力。
一個(gè)組件節(jié)點(diǎn)在 React 中很難被判斷為沒(méi)有發(fā)生過(guò)更新。因?yàn)?props 的比較總是不同的。它的比較方式如下。
{} === {} // false
因此,高級(jí) React 開(kāi)發(fā)者需要非常了解 React 的默認(rèn)優(yōu)化機(jī)制,讓 props 的比較不發(fā)生,因?yàn)橐坏┌l(fā)生,那么結(jié)果必定是 false。
i
事實(shí)上,對(duì) React 默認(rèn)優(yōu)化機(jī)制了解的開(kāi)發(fā)者非常少,我們?cè)陂_(kāi)發(fā)過(guò)程中也不會(huì)為了優(yōu)化這個(gè)性能去重新調(diào)整組件的分布。更多的還是使用 memo 與 useMemo/useCallback 暴力緩存節(jié)點(diǎn)。
在這樣的背景之下,冗余的 re-render 在大量的項(xiàng)目中發(fā)生。這也是為什么 React 總是唄吐槽性能不好的主要原因。當(dāng)然,大多數(shù)項(xiàng)目并沒(méi)有頻繁更新 state 的需求,因此這一點(diǎn)性能問(wèn)題表現(xiàn)得并不是很明顯。
如果我們要解決冗余 re-render 的問(wèn)題,需要對(duì) React 默認(rèn)優(yōu)化技能有非常深刻的理解,需要對(duì) memo、useCallback、useMemo 有準(zhǔn)確的理解。但是普通的 React 開(kāi)發(fā)者很難理解他們,有的開(kāi)發(fā)者雖然在項(xiàng)目中大量使用了,但是未必就達(dá)到了理想的效果。React Compiler 則是為了解決這個(gè)問(wèn)題,它可以自動(dòng)幫助我們記憶已經(jīng)存在、并且沒(méi)有發(fā)生更新的組件,從而解決組件冗余 re-render 的問(wèn)題。
從使用結(jié)果的體驗(yàn)來(lái)看,React Compiler 被集成在代碼自動(dòng)編譯中,因此只要我們?cè)陧?xiàng)目中引入成功,就不再需要關(guān)注它的存在。我們的開(kāi)發(fā)方式不會(huì)發(fā)生任何改變。它不會(huì)更改 React 現(xiàn)有的開(kāi)發(fā)范式和更新方式,侵入性非常弱。
并非所有的組件都能被優(yōu)化。因此早在 React 18 的版本中,React 官方團(tuán)隊(duì)就提前發(fā)布了嚴(yán)格模式。在頂層根節(jié)點(diǎn)中,套一層 StrictMode 即可。
<StrictMode> <BrowserRouter> <App /> </BrowserRouter></StrictMode>
遵循嚴(yán)格模式的規(guī)范,我們的組件更容易符合 React Compiler 的優(yōu)化規(guī)則。
我們可以使用如下方式首先檢測(cè)代碼庫(kù)是否兼容。在項(xiàng)目根目錄下執(zhí)行如下指令。
npx react-compiler-healthcheck
?
該腳本主要用于檢測(cè)。
1、項(xiàng)目中有多少組件可以成功優(yōu)化:越多越好。
2、是否使用嚴(yán)格模式,使用了優(yōu)化成功率更高。
3、是否使用了與 Compiler 不兼容的三方庫(kù)。
例如,我的其中一個(gè)項(xiàng)目,檢測(cè)結(jié)果如下:
很棒。
這里需要注意的是,引入了 Compiler 插件之后,它會(huì)自動(dòng)工作,我們完全不用關(guān)注它的存在。因此,如果程序不出問(wèn)題,對(duì)于開(kāi)發(fā)者來(lái)說(shuō),編譯工作是無(wú)感的。所以開(kāi)發(fā)體驗(yàn)非常棒。
!
不過(guò)有一些美中不足的是,當(dāng)我嘗試驗(yàn)證其他已經(jīng)寫(xiě)好的組件被編譯之后是否存在問(wèn)題時(shí),發(fā)現(xiàn)有一個(gè)組件的運(yùn)行邏輯發(fā)生了變化。目前我還沒(méi)有深究具體是什么原因?qū)е碌模贿^(guò)通過(guò)對(duì)比,這個(gè)組件的獨(dú)特之處在與,我在該組件中使用了 useDeferredValue 來(lái)處理異步請(qǐng)求。
另外,Compiler 也不能阻止 context 組件的 re-render。例如我在一個(gè)組件中使用了 use(context) ,哪怕我并沒(méi)有使用具體的值。如下所示。
import {use} from 'react'import {Context} from './context'export default function Card() { const value = use(Context) console.log('xxxxx context') return ( <> <div className='_06_card'> <div className="title">Canary</div> <p>The test page</p> </div> </> )}
理想情況是這種情況可以不用發(fā)生 re-render。因此總體來(lái)說(shuō),Compiler 目前確實(shí)還不能完全信任。也有可能我還沒(méi)掌握正確的姿勢(shì),還需要對(duì)他有更進(jìn)一步的了解才可以。
不過(guò)值得高興的是,新項(xiàng)目可以放心使用 Compiler,因?yàn)檫\(yùn)行結(jié)果我們都能實(shí)時(shí)感知、調(diào)試、調(diào)整,能最大程度的避免問(wèn)題的出現(xiàn)。
React Compiler 編譯之后的代碼并非是在合適的時(shí)機(jī)使用 useMemo/memo 等 API 來(lái)緩存組件。而是使用了一個(gè)名為 useMemoCache 的 hook 來(lái)緩存代碼片段。
Compiler 會(huì)分析所有可能存在的返回結(jié)果,并把每個(gè)返回結(jié)果都存儲(chǔ)在 useMemoCache 中。如上圖所示,他打破了原有的鏈表存儲(chǔ)結(jié)果,而選擇把緩存結(jié)構(gòu)存儲(chǔ)在數(shù)組上。因此在執(zhí)行效率上,Compiler 之后的代碼會(huì)高不少。每一個(gè)渲染結(jié)果都會(huì)被存儲(chǔ)在 useMemoCache 的某一項(xiàng)中,如果判斷之后發(fā)現(xiàn)該結(jié)果可以復(fù)用,則直接通過(guò)讀取序列的方式使用即可。如圖所示。
因此,編譯之后的代碼看上去會(huì)更加的繁雜。但是執(zhí)行卻會(huì)更加高效。
初次感受下來(lái),雖然感覺(jué)還不錯(cuò)。但是依然會(huì)有一種自己寫(xiě)的代碼被魔改的不適感。特別是遇到問(wèn)題的時(shí)候,還不知道到底編譯器干了什么事情讓最終運(yùn)行結(jié)果與預(yù)想的完全不同。
i
這也是我不太喜歡使用 Solid 與 Svelte 的根本原因。不過(guò) React 好在可以不用...
但是從執(zhí)行性能上確實(shí)會(huì)有大的提高,這一點(diǎn)對(duì)于初學(xué)者可能會(huì)比較友好。
目前,由于接觸時(shí)間太短,我對(duì)于 React Compiler 的使用體驗(yàn)還停留在比較淺的層面。因此能聊的東西并不多,在后續(xù)我有了更進(jìn)一步更深刻的體會(huì)之后,再來(lái)跟大家分享體驗(yàn)結(jié)果。
本文鏈接:http://www.tebozhan.com/showinfo-26-94294-0.html苦等三年,React Compiler 終于能用了。使用體驗(yàn):很爽,但仍有瑕疵
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com