本文旨在整理常見Web前端性能優(yōu)化的思路,可供前端開發(fā)參考。因為力求精簡,限于篇幅,所以并未詳述具體實施方案。
基于現(xiàn)代Web前端框架的應(yīng)用,其原理是通過瀏覽器向服務(wù)器發(fā)送網(wǎng)絡(luò)請求,獲取必要的index.html和打包好的JS、CSS等資源,在瀏覽器內(nèi)執(zhí)行JS,動態(tài)獲取數(shù)據(jù)并渲染頁面,從而將結(jié)果呈現(xiàn)給用戶。
在這個過程中,有兩個步驟可能較為耗時,一個是網(wǎng)絡(luò)資源的加載,另一個是瀏覽器內(nèi)代碼執(zhí)行和DOM渲染。
而耗時的增加會導(dǎo)致頁面響應(yīng)慢,卡頓,影響用戶體驗。
針對上述兩種耗時的情況,常見的優(yōu)化方向有:
- 縮短請求耗時;
- 減少重排重繪;
- 改善JS性能。
1 縮短請求耗時
網(wǎng)絡(luò)資源是Web應(yīng)用運行的基礎(chǔ),改善網(wǎng)絡(luò)資源加載速度會顯著改善前端性能。
1.1 優(yōu)化打包資源
總體原則:減少或延遲模塊引用,以減少網(wǎng)絡(luò)負荷。
常用工具:
- webpack
- webpack-bundle-analyzer可視化分析工具
常用方法:
-
減小體積:減少非必要的
import
;壓縮JS代碼;配置服務(wù)器gzip等;使用WebP圖片; -
按需加載:可根據(jù)“路由”、“是否可見”按需加載JS代碼,減少初次加載JS體積。比如可以使用
import()
進行代碼分割,按需加載; - 分開打包:利用瀏覽器緩存機制,依據(jù)模塊更新頻率分層打包。
其他方法:
- 雪碧圖:每個HTTP/1.1請求都是獨立的TCP連接,最大6個并發(fā),所以合并圖片資源可以優(yōu)化加載速度。HTTP/2已經(jīng)不需要這么做了。
1.2 CDN加速
總體原則:通過分布式的邊緣網(wǎng)絡(luò)節(jié)點,縮短資源到終端用戶的訪問延遲。
常用工具:
- Cloudflare
- AWS CloudFront
- Aliyun CDN
常用方法:
- 加速圖片、視頻等大體積文件
1.3 瀏覽器緩存
總體原則:避免重復(fù)傳輸相同的數(shù)據(jù),節(jié)省網(wǎng)絡(luò)帶寬,加速資源獲取。
常用方法:
可以通過設(shè)置HTTP Header來控制緩存策略,一般有如下幾種。
-
強緩存
-
Expires
:HTTP/1.0 -
Cache-Control
:HTTP/1.1
-
-
協(xié)商緩存
-
ETag
+If-None-Match
-
Last-Modified
+If-Modified-Since
-
拿ETag舉例,如果瀏覽器給的If-None-Match
值與服務(wù)端給的ETag
值相等,服務(wù)器就直接返回304
,從而避免重復(fù)傳輸數(shù)據(jù)。
ETag示例:
如果幾個配置同時存在,則優(yōu)先級為:Cache-Control
>Expires
>ETag
>Last-Modified
。
1.4 更高版本的HTTP
總體原則:使用高版本HTTP提升性能。
常用工具:
- HTTP/2
HTTP/2較HTTP/1.1最大的改進在于:
- 多路復(fù)用:單一TCP連接,多HTTP請求,有Demo;
- 頭部壓縮:減少HTTP頭體積;
- 請求優(yōu)先級:優(yōu)先獲取重要的數(shù)據(jù);
- 服務(wù)端推送:主動推送CSS等靜態(tài)資源。
其他方法:
- HTTP/3
HTTP/3基于UDP,有很多方面的性能改進,如多路復(fù)用無隊頭阻塞,響應(yīng)更快。感興趣的同學(xué)可參考Wiki。
1.5 Web Socket
總體原則:解決HTTP協(xié)議無法實時通信的問題。
Web Socket是一條有狀態(tài)的TCP長連接,用于實現(xiàn)實時通信、實時響應(yīng)。
1.6 服務(wù)器端渲染(SSR)
總體原則:第一次訪問時,服務(wù)器端直接返回渲染好的頁面。
一般流程:
- 瀏覽器向 URL 發(fā)送請求;
-
服務(wù)器端返回“空白”
index.html
; - 瀏覽器不能呈現(xiàn)頁面,需要繼續(xù)下載依賴;
- 加載所有腳本后,組件才能被渲染。
SSR流程:
- 瀏覽器向 URL 發(fā)送請求;
- 服務(wù)器端執(zhí)行JS完成首屏渲染并返回;
- 瀏覽器直接呈現(xiàn)頁面,然后繼續(xù)下載其他依賴;
- 加載所有腳本后,組件將再次在客戶端呈現(xiàn)。它將對現(xiàn)有View進行合并。
常用工具:
- Node.js,用于服務(wù)器端執(zhí)行代碼,輸出HTML給瀏覽器,支持所有主流前端框架
- Next.js,用于服務(wù)器端渲染React的框架
- gatsby,用React生成靜態(tài)網(wǎng)站的工具
除了可以提升頁面用戶體驗,還能應(yīng)用于SEO。
2 減少重排重繪
除了網(wǎng)絡(luò)資源以外,另一個影響前端性能的因素就是前端頁面的渲染繪制效率。
雖然不同的前端框架有一些差異,但整體的優(yōu)化思路是一致的,這里將以React舉例。
2.1 減少渲染量
總體原則:不渲染未展示的部分。
常用工具:
- react-window
- react-loadable
-
JS原生,如
IntersectionObserver
-
框架提供,如
React.lazy
、react-intersection-observer
常用方法:
- 虛擬列表:只渲染可見區(qū);
- 惰性加載:無限滾動;
- 按需加載:頁面只在切換過去時才加載。
以虛擬列表舉例,以下是使用react-window
庫,僅僅渲染了可見區(qū)的數(shù)據(jù):
2.2 減少渲染次數(shù)
總體思路:避免重復(fù)的渲染。
常用工具:
- lodash
- JS或框架自帶
常用方法:
- 防抖與節(jié)流;
- 對于React函數(shù)組件來說,合理使用副作用,拆分無關(guān)聯(lián)的副作用;
-
對于React類組件來說,可以使用
shouldComponentUpdate
或使用PureComponent
來優(yōu)化渲染; - 利用緩存,如React.memo;
-
使用requestAnimationFrame替代
setInterval
執(zhí)行動畫。
3 改善JS性能
因為瀏覽器是單線程異步模型,長時間的運算會阻塞渲染過程,所以改善復(fù)雜運算有助于改善前端的整體性能。
3.1 緩存復(fù)雜計算
總體思路:避免重復(fù)計算。
常用方法:
-
對于React函數(shù)組件來說,可以使用
useMemo
緩存復(fù)雜計算值。
舉例如下,memoizedValue
需要經(jīng)過復(fù)雜計算才能得到,此時就可以使用useMemo
緩存,僅僅在輸入參數(shù)發(fā)生變化時才重新計算,避免計算阻塞頁面渲染,從而避免頁面卡頓。
1const MyFunctionalComponent = () => {
2 const memoizedValue = useMemo(() => {
3 computeExpensiveValue(a, b);
4 }, [a, b]);
5
6 return ;
7}
但useMemo
自身也有性能消耗,需要視情況使用,某些場景可以利用React的渲染機制避免性能問題,可以參考《Before You memo()》。
3.2 Web Worker
總體原則:多線程思想。
常用方法:
- Dedicated Workers,處理與UI無關(guān)的密集型數(shù)學(xué)計算:大數(shù)據(jù)集合排序、數(shù)據(jù)壓縮、音視頻處理;
- Service Worker,服務(wù)端推送,或者PWA中配合CacheStorage在前端控制緩存資源;
- Shared Worker,Tab間通信。
JS語言在設(shè)計之初就是單線程異步模型,好處是可以高效處理I/O操作,但壞處是無法利用多核CPU。
Web Worker會啟動系統(tǒng)級別的線程,可進行多線程編程,發(fā)揮多核的性能。
3.3 Web Assembly
總體原則:將復(fù)雜的計算邏輯編譯為Web Assembly,避免JS類型推斷過程中的性能開銷,可用于性能的極限優(yōu)化。
適用范圍有限:
曾在網(wǎng)上看到,有人使用自頂向下非優(yōu)化的斐波那契數(shù)列算法來舉例,說Web Assembly比原生JS快一倍,實測之后似乎也沒有。
在同一臺機器測試,其中求第48個值的耗時如下:
- C(Ubuntu+GCC):18s
- JS(V8):32s
- Web Assembly(V8+EMCC):39s
一種可能的猜想是,斐波那契計算中沒有大量的類型推斷,而且V8內(nèi)部有一些優(yōu)化機制,使得此處JS執(zhí)行速度快于Web Assembly。
簡而言之,并非所有場景都適用于Web Assembly。
另一種運用場景是,把不同語言編寫的代碼(C/C++/Java等)編譯為Web Assembly,能以接近原生的速度在Web中運行,并且與JS共存。
總結(jié)
導(dǎo)致前端性能問題的因素是多方面的。
如果是前端資源加載慢,導(dǎo)致頁面慢,則應(yīng)該考慮如何縮短請求耗時。而如果是前端頁面邏輯笨重,UI數(shù)據(jù)量太大,則可以試著從減少重排重繪的角度去優(yōu)化。對于耗時長的復(fù)雜計算,緩存計算結(jié)果往往是見效較快的優(yōu)化方式。
最后需要注意的是,在實際應(yīng)用開發(fā)過程中,因為受限于開發(fā)成本,所以需要平衡優(yōu)化所花的代價與其對應(yīng)產(chǎn)生的成效。可以有針對性地對性能瓶頸進行分析和處理,同時也需要避免引入不必要的優(yōu)化措施,以確保最終優(yōu)化效果。
-
性能
+關(guān)注
關(guān)注
0文章
271瀏覽量
19012
發(fā)布評論請先 登錄
相關(guān)推薦
評論