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