被Diss性能差,Dan連夜優(yōu)化React新文檔
大家好,我卡頌。
昨天在開源圈發(fā)生個(gè)小插曲。起因是有個(gè)用戶表示:React新文檔在文檔結(jié)構(gòu)、美觀度、性能等各方面都達(dá)到很高的標(biāo)準(zhǔn)。
尤雨溪對(duì)Vue新文檔與React Beta文檔做了測(cè)試后表示:在性能這塊,Vue新文檔更具優(yōu)勢(shì)。
左Vue,右React
Dan表示:當(dāng)前文檔還處于Beta版本,現(xiàn)在有更重要的工作要完成,正式版上線前會(huì)優(yōu)化性能。
話雖這么說,Dan應(yīng)該是通了個(gè)宵優(yōu)化了一把性能:
本篇文章我們來看看,Dan都做了哪些優(yōu)化。
優(yōu)化效果
經(jīng)過優(yōu)化后的lightHouse跑分:
作為對(duì)照,Vue文檔的跑分:
兩者10次TTI跑分對(duì)比:
這里的TTI[1](Time to Interactive,即可交互時(shí)間),衡量的是「頁面變得完全可交互所需時(shí)間」,其中「完全可交互」指:
- 頁面展示了「有用信息」(由FCP衡量,F(xiàn)CP指First Contentful Paint)。
- 可見頁面中大部分元素完成事件綁定,交互響應(yīng)的延遲在50ms內(nèi)。
優(yōu)化措施
優(yōu)化主要有兩個(gè)思路:
- 編譯時(shí):減少打包體積。
- 運(yùn)行時(shí):「非首屏必需」代碼延遲加載。
編譯時(shí)優(yōu)化
之前入口處全量引入了一個(gè)工具函數(shù)utils,現(xiàn)在將其中方法拆分成不同文件,這樣能減少首屏bundle體積:
再比如:
這部分優(yōu)化讓bundle體積減少約一半:
其次,當(dāng)前Next.js(文檔使用的框架)沒有默認(rèn)開啟「針對(duì)現(xiàn)代瀏覽器編譯」。這意味著bundle中會(huì)引入更多polyfill,有更多語法轉(zhuǎn)換及幫助函數(shù)。
Dan通過配置開啟了這個(gè)功能:
運(yùn)行時(shí)優(yōu)化
運(yùn)行時(shí)優(yōu)化的方式主要是:懶加載非必需資源。
新文檔中存在很多codesandbox(在線示例),這些示例依賴CodeMirror linter,但這不是首屏必需的。
所以Dan將這部分資源懶加載:
除此之外,如果你細(xì)心觀察會(huì)發(fā)現(xiàn),Total Blocking Time指標(biāo)下降很多:
左之前,右之后
TBT[2](Total Blocking Time,即總阻塞時(shí)間)測(cè)量頁面「被阻止響應(yīng)用戶輸入(例如鼠標(biāo)點(diǎn)擊、屏幕點(diǎn)擊或按下鍵盤)的總時(shí)間」。
一般來說,如果JS執(zhí)行時(shí)間過長,就會(huì)影響這個(gè)指標(biāo)。
我們知道,頁面加載后前端框架會(huì)有首屏渲染的初始化過程。即使是服務(wù)端渲染,也會(huì)有Hydrate(注水)的過程。
而React18的Selective Hydration為解決這一問題提供了好方法。
如果你的React18應(yīng)用是SSR,那么被包裹的組件部分不會(huì)參與首次Hydrate的過程。
也就是說,被包裹的部分不會(huì)影響阻塞時(shí)間。
所以,雖然這部分工作很重要,但Dan需要做的,僅僅是把一些「對(duì)首屏顯示不太重要的組件」包裹在中。
可以看到,在將一些組件用包裹前Hydrate作為一個(gè)長任務(wù)的耗時(shí):
當(dāng)包裹之后,這個(gè)長任務(wù)持續(xù)時(shí)間顯著降低,進(jìn)而降低TBT:
總結(jié)
這些只是初步的優(yōu)化結(jié)果,后續(xù)還有很多優(yōu)化工作值得去做。比如INP[3](Interaction to Next Paint,與下一次Paint的交互)指標(biāo)還是偏高:
Dan坦言:指標(biāo)偏高的原因可能是因?yàn)?—— React本身比較慢。
他對(duì)此提出了一些猜想,你可以到這里參與討論[4]。
參考資料
[1]TTI:https://web.dev/tti/。
[2]TBT:https://web.dev/tbt/。
[3]INP:https://web.dev/inp/。
[4]參與討論:https://github.com/reactjs/reactjs.org/issues/4691。