前端性能優化不管是在面試中還是在實際開發過程中,都是每一個前端開發工程師都必不可少的能力。本文總結本人多年開發經驗中對前端性能優化的了解,希望對大家有所幫助,因涉及的優化方向較多,針對某些細節不再詳細說明,大家有興趣的可深入了解,話不多說,正文開始。
重要性
一個好的前端項目性能非常重要,特别是面向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數量不可太分散,避免一下發起太多的請求,必要将部分資源合并在一起,減少請求的數量。但是在合并的過程中需求在體積和數量之間權衡,并不是越少越好,可将最大的體積控制在一個範圍内進行合并
- 部分小體量級别的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原生能力優化等。以上優化方向的說明就較為簡潔,具體的實操及原理有興趣的同學可以多研究,面對這麼多的方向優化,究竟如何選擇呢。
沒有所謂的絕對優化,都需要結合目前項目的應用場景及對項目全量的性能分析,找到某個方向的不足,針對性的優化,選擇合适的方案。希望大家都能找到自己合适的優化方向,把項目優化的妥妥的。看完本文如果覺得有用,記得點個贊支援,收藏起來說不定哪天就用上啦~