近期對前端性能優化的總結
前端性能優化不管是在面試中還是在實際開發過程中,都是每一個前端開發工程師都必不可少的能力。本文總結本人多年開發經驗中對前端性能優化的理解,希望對大家有所幫助,因涉及的優化方向較多,針對某些細節不再詳細說明,大家有興趣的可深入了解,話不多說,正文開始。
重要性
一個好的前端項目性能非常重要,特別是面向ToC的用戶,好的用戶體驗可以極大的提高業務轉化率,從而性能的好壞關乎到業務的營收。對于一個商業性公司只要是和錢相關,都是極其敏感且重要的。
度量方式
2020年 Google 提出了新一代 Web 性能體驗指標 Core Web Vitals,其中包括了 LCP、FID、CLS 三大指標。
- Largest Contentful Paint (LCP): 衡量加載體驗:為了提供良好的用戶體驗, LCP 應該在頁面首次開始加載后的 2.5 秒內發生。
- First Input Delay (FID): 衡量可交互性,為了提供良好的用戶體驗,頁面的 FID 應當小于 100毫秒。
- Cumulative Layout Shift (CLS):衡量視覺穩定性,為了提供良好的用戶體驗,頁面的CLS應保持小于 0.1。
針對這些指標直接可以通過瀏覽器開發工具中的Lighthouse得出是否達到標準。通過這樣的方式得出的結論快速直觀,對原本網站無侵入,不影響真實用戶的性能。但是也有缺點,不支持復雜的業務邏輯場景,監測的數據量太小,不能還原大部分真實用戶的使用情況。
所以為了得到真實且全面的數據,大部分的公司都會開發一套監測方案,或者使用第三方監測平臺,這樣會對網站有一定的性能影響,但是可通過更全面的性能數據分析可優化的方向。
性能優化方向
基于上面的三個體驗指標,我們可以從頁面加載的生命周期進行優化,頁面加載前的預處理,加載過程中,頁面渲染時,用戶界面交互等幾個階段,下面將針對不同的階段進行優化,大家可根據自己項目的情況針對性的選擇優化。
加載前的預處理
使用 dns-prefetch、preconnect 減少DNS解析,建立TCP連接以及執行TLS握手時間,dns-prefetch: 告知瀏覽器對指定域名進行DNS解析。當后續請求該域名資源時可省掉DNS解析的時間。preconnect: 告知瀏覽器與指定域名的服務器建立連接。當后續請求該域名資源時,可直接使用已建立好的連接,省掉了 DNS+TCP+TLS 的時間
<link rel="dns-prefetch" href="https://s1.static.com">
<link rel="preconnect" href="https://s1.static.com">
使用 preload/prefetch 讓瀏覽器提前加載需要的資源,preload可以指明哪些資源是在頁面加載完成后即刻需要的,瀏覽器在主渲染機制介入前就進行預加載,這一機制使得資源可以更早的得到加載并可用,且更不易阻塞頁面的初步渲染,進而提升性能;prefetch其利用瀏覽器空閑時間來下載或預取用戶在不久的將來可能訪問的文檔。切記不要將 preload 和 prefetch 進行混用,它們適用于不同的場景,如對同一個資源同時使用 preload 和 prefetch 會造成不必要的二次下載。
<link href="xx.js" rel="prefetch">
<!--as表示指定資源類型-->
<link href="xx.js" rel="preload" as="script">
加載過程中
1. 盡可能的減小資源的大小
- 業務代碼本身盡可能的不要重復,提高組件化的使用,提示代碼的復用率,這里不止是JS,CSS樣式也是一樣
- 壓縮靜態資源,一般腳手架都默認會處理,自建項目可檢查是否有壓縮
- html中的DOM層級控制不要太深以及減少不必要的DOM使用,盡可能發揮偽元素及CSS的使用
- 檢查項目的依賴包是否有重復引用的情況,不同的依賴包可能引用了同一個不同版本的包,可通過webpack-bundle-analyzer插件分析查看
- UI組件庫或其他庫使用babel-plugin-import插件進行按需加載
- 組件按需加載,使用AsyncComponent僅加載首屏組件
- 動態導入第三方比較大的模塊,import('/modules/echart.js) .then((module) => {}),但不要濫用,結合實際場景使用
- 減小第三方庫的大小,如Moment.js/lodash等,使用輕量級別替代方案或者自己重新實現
- 對首評秒開要求較高的,可對首屏請求的接口進行拆分,快速響應首屏需要用到的字段,其他的數據異步加載
- 使用tree shaking,當我們在項目中引入其他模塊時,他會自動將我們用不到的代碼,或者永遠不會執行的代碼搖掉,在Uglify階段查出,不打包到bundle中
- HTTP頭部Cookie的精簡,去除不必要的Cookie,靜態資源做獨立域名部署,避免請求攜帶Cookie
- HTTP頭部開啟gzip壓縮,可大大減小網絡傳輸的數據量
- HTTP頭部開啟keep-alive
- 升級HTTP到2.0,2.0的頭部壓縮,減少了數據傳輸量,能夠節省消息頭占用的網絡的流量,且還有多路復用等優勢
2. 盡可能的減少資源的次數
- 部分小體量級別的JS/CSS可內聯到HTML中,減少請求數量
- 減小預檢請求OPTIONS的發起,可通過服務端設置Access-Control-Max-Age字段或改為發起簡單請求
- 取消無效請求,表單提交頻繁點擊,路由切換時還有未完成的請求。這些都會產生無效請求,對服務器和用戶體驗都是不好的
- 緩存策略
開啟http強緩存與協商緩存,對于不同類型的資源使用不同的緩存策略
靜態資源開啟CDN服務
對于不常變化的數據包括外部JS/CSS資源,可進行前端瀏覽器緩存,減少請求,但此類緩存需設定好清除及更新的機制
3. 其他資源優化
- 圖片webp使用,對于支持的設備使用webp
- 圖片裁剪,針對使用場景進行相應的裁剪
- 大圖不要打包在項目中,上傳到單獨的靜態資源服務器或是CDN中
- 圖片上傳前進行壓縮,切記不要使用原圖
- 設置圖片標簽尺寸大小,防止圖片加載中導致頁面布局抖動,影響CLS指標的數值
- 超出屏幕外的圖片開啟懶加載
- 對于項目中大量的小圖標可使用iconfont字體方案
- 使用第三方字體庫時盡可能按需文字生成
- 加載字體的時候會導致頁面文字有一定的閃爍抖動,可在進入需要用到的頁面前使用preload提前進行加載
頁面渲染時
開啟骨架屏,提升用戶體驗,避免加載到渲染過程中都是白屏階段
對于大量列表的滾到使用虛擬列表
盡量多使用CSS3動畫
使用 requestAnimationFrame 監聽幀變化,使得在正確的時間進行渲染
合理使用CSS,避免通配符,最大化樣式繼承,少用標簽選擇器,減少過深嵌套等
用戶界面交互
- 減少頁面重排、重繪
- 防抖節流的使用
- 合理使用 requestAnimationFrame 動畫代替 setTimeout
- 開啟GPU加速,CSS中可使用以下屬性(CSS3 transitions、CSS3 3D transforms、Opacity、Canvas、webGL、Video)來觸發 GPU 渲染
- 減少 JavaScript 腳本執行時間,把一些和 DOM 操作無關且耗時的任務放到 Web Workers 中去執行
- 對未來某個時間內需要執行動畫的元素,將其標記為 will-change,這樣渲染引擎會將該元素單獨生成一個圖層
最后
本文對前端性能優化的方向列舉了不少,除此之外也還有很多是沒有涉及到的,比如小程序內,Vue/React框架中特殊的其他優化,配合App原生能力優化等。以上優化方向的說明就較為簡潔,具體的實操及原理有興趣的同學可以多研究,面對這么多的方向優化,究竟如何選擇呢。
沒有所謂的絕對優化,都需要結合當前項目的應用場景及對項目全量的性能分析,找到某個方向的不足,針對性的優化,選擇合適的方案。希望大家都能找到自己合適的優化方向,把項目優化的妥妥的。