作者: vivo 互聯網大數據團隊- Wang Lei
本文是vivo互聯網大數據團隊《BI 數據可視化平臺建設》系列文章第3篇。
隨著越來越多代碼的堆積,平臺的運行加載性能也在逐步下降,在不同程度上極大地影響了用戶體驗,從而導致用戶流失。本文就是從這樣的一個背景出發,通過對BI數據可視化平臺的一系列的性能優化實踐,給大家系統性闡述首頁性能優化的核心策略,并探討在日常開發中如何實現長效性能保障。
往期系列文章:
-
BI 數據可視化平臺建設(1)—交叉表組件演變實戰
-
BI 數據可視化平臺建設(2)—篩選器組件升級實踐
文章太長?1分鐘看圖抓住核心觀點👇
一、背景
隨著業務的拓展和用戶數量的激增,平臺經歷了多個周期的快速迭代,整體功能場景也越發復雜的。在快速迭代的過程中,我們很容易忽略平臺的性能,到了某一個節點,猛地發現,隨著越來越多代碼的堆積,平臺的運行加載性能也在逐步下降,在不同程度上極大地影響了用戶體驗,從而導致用戶流失。本文就是從這樣的一個背景出發,通過對BI數據可視化平臺的一系列的性能優化實踐,給大家分享一下如何提升首頁性能的思路,并且讓我們在日常開發中,如何持續保持高性能,而不是又一次回過頭來優化性能。本文主要給大家介紹一平臺在進行首頁性能提升的一些實踐經驗。
二、了解性能指標
2.1 用戶體驗核心指標
衡量一個 Web 頁面的體驗和質量有非常多的指標,根據頁面加載流程可以將指標分成三大類:
-
文檔加載相關(TTFB、DCL等)
-
內容呈現相關(LCP、FCP、FMP 等)
-
交互響應相關(INP、FPS 等)
針對這么多的性能指標,Google 提出了網站用戶體驗的三大核心指標 (LCP INP CLS),分別用來衡量用戶感知的加載速度、量化網頁首次互交互的感受、衡量網頁視覺的穩定性:
圖片來源:https://web.dev/
2.2 平臺度量指標
但是在實際規劃平臺性能度量體系時,我們可以根據自身的業務和需求進行自定義,針對數據可視化平臺來說,我們更看重用戶感知的加載速度,如何讓用戶快速看到數據可視化內容是我們首頁性能優化的關鍵,因此我們性能指標主要以文檔加載和內容呈現為主,這里我們以 TTFB、FCP、LCP 作為我們首頁性能的度量指標,它們涵蓋了網絡請求到頁面主要內容加載的過程:
網絡請求過程(網絡響應衡量指標 TTFB):
TTFB 主要指的是以下請求階段耗時的總和:
-
重定向時間
-
Service Worker 啟動時間(如果有)
-
DNS 查找
-
連接和 TLS 協商
-
請求,直到響應的第一個字節到達
頁面主要內容加載過程(內容呈現衡量指標 FCP、LCP):
圖片來源:https://web.dev/
- TTFB(Time to First Byte ):
它主要測量的是在網絡請求階段中,從請求資源到響應的第一個字節到達所經過的時間,這有助于識別 Web 服務器因速度過慢而無法響應請求。由于 TTFB 發生在指標 FCP(First Contentful Paint ) 和 LCP(Largest Contentful Paint)之前,因此希望服務器能夠快速地響應導航請求。一般來說,大多數網站都應盡量將 TTFB 控制在 0.8秒 以內,且超過75%以上PV 達到該范圍。
圖片來源:https://web.dev/
- FCP(First Contentful Paint):
它用于標記網頁加載過程中用戶可以在屏幕上看到的第一個元素所用的時間。元素主要是指文本、圖片(包括背景圖片)、SVG 或 Canvas。可以用于衡量用戶感知的加載速度。為了提供良好的用戶體驗,網站的 FCP 最好不要超過 1.8 秒,且確保超過75%以上PV 達到該范圍。
圖片來源:https://web.dev/
- LCP(Largest Contentful Paint):
它用于標記網頁加載過程中加載了網頁主要內容的時間點。可以用于衡量用戶感知的加載速度,也是Google 提出的度量用戶體驗的三大核心指標之一。為了提供良好的用戶體驗,網站的 LCP 最好控制在 2.5 秒 以內,且確保超過75%以上PV 達到該范圍。
圖片來源:https://web.dev/
三、首頁性能現狀
**背景:**性能問題主要是由于前期開發人力有限、功能快速迭代等原因,導致代碼質量和可維護性較差,積累下的技術債務。
(1)平臺性能指標分析:LCP (首屏平均耗時) 高達3.3s,遠高于Google LCP衡量的標準(2.5s)。
(2)通過Lighthouse工具進行性能分析,性能得分較低,各項性能指標都處于不及格的水平。
四、分析性能問題
4.1 通過Network面板分析
從Network面板上,可以分析得出加載過程中存在以下幾類問題:
-
入口文件體積太大,加載耗時長,阻塞其他資源加載
-
低優先級資源阻塞了高優先級資源加載
-
微前端子應用等非首屏依賴資源沒有進行異步加載
-
網絡傳輸協議還處于HTTP1.1,傳輸效率低
-
接口請求傳輸的數據體過大,存在大量冗余數據
4.2 通過Performance面板分析
從 Performance 面板上,可以分析得出加載過程中存在以下幾類問題:
-
FPS 長時間出現紅色條形,表示幀速率下降得太低,可能出現動畫延遲卡頓等問題
-
CPU 資源占用率過高,可能出現性能問題
-
主線程存在多個 Long Task(長任務),阻塞了頁面加載渲染
4.3 通過Lighthouse工具分析
通過 Lighthouse 工具,可以分析得出加載過程中存在以下的問題:
-
大量 Long Task 阻塞了主線程工作
-
存在阻塞渲染的低優先級資源
-
DOM節點數過多,增加了內存占用,樣式計算用時延長,并產生高昂的布局重排成本
-
圖片資源不是最優壓縮效果的格式
-
存在大量未使用的CSS和JS文件代碼
五、優化實踐
5.1 優化方向 (時間和空間)
通過上述的問題分析,我們可以分析出資源加載渲染耗時 以及瀏覽器性能資源占有 都有可能導致頁面卡頓緩慢,影響用戶體驗,因此可以從耗時和資源占用兩方面來進行性能優化,也可以理解成時間和空間的優化。
5.2 時間優化 (網絡耗時、加載耗時、渲染耗時等)
(1)網絡傳輸耗時優化
網絡傳輸耗時優化主要可以從 緩存策略、傳輸協議、資源預加載預解析、CDN 等幾個方向進行。本次優化主要是通過網絡傳輸升級、資源預加載、請求性能優化等方面來講解一下。
-
網絡傳輸協議升級,由 HTTP/1.1 升級至 HTTP/2.0 版本,通過 HTTP/2.0 多路復用的特性解決了請求并發數限制的問題,同時二進制傳輸和頭部壓縮等特性也提高了網絡傳輸的效率。
-
刪除資源預加載(Preload),減少首頁非關鍵資源的預加載處理。通過加載瀑布流可以看到,這里提前加載了多個非首屏關鍵資源的字體文件,且文件體積高達 1.8MB,阻塞了首屏關鍵資源的加載解析。所以我們需要根據資源的優先級,合理的使用 Preload(預加載)和 Prefetch(預解析)。
-
請求性能優化,降低請求響應耗時。通過Network面板可以看到,首頁依賴的主要接口返回的數據體在沒壓縮前高達 3.1 MB,這里我們對請求內容進行了分析,通過異步請求、減少非關鍵的冗余數據等處理將傳輸數據體積降低到了 500KB 內。除了減少數據傳輸量,我們還可以通過請求合并,利用緩存等減少通信次數來進行請求性能優化。
(2)資源加載耗時優化
資源加載耗時優化可以從 代碼壓縮、代碼分包、組件、工具庫、ICON等按需加載等幾個方向進行。主要是通過優化體積來減少資源加載耗時,從而提升首屏性能。
-
首先通過 webpack-bundle-analyzer 插件對包體積進行分析,可以看到 chunk-vendors.js 文件體積較大,同時還存在依賴嵌套等問題,導致資源加載緩慢。本次優化我們通過代碼分包、資源按需加載,圖片格式優化等措施,減少了資源體積, chunk-vendors.js 文件也從 2.3MB 降低到 480KB。下面我們通過幾個具體示例進行講解:
對 Echarts 、Ant-Design-Vue-1.x ICON等UI工具庫進行按需加載。
// 優化前 在入口文件進行全量的同步加載
import * as echarts from ‘echarts/core’;
import { XXXChart } from ‘echarts/charts’;
import { XXXComponent } from ‘echarts/components’;
import { CanvasRenderer } from ‘echarts/renderers’;echarts.use([XXXChart, XXXComponent, CanvasRenderer]);
// 優化后 根據使用場景進行按需加載
async function initEcharts(chartType){
const echarts = await import(‘echarts/core’);
const { XXXChart} = await import(‘echarts/charts’);
const { XXXComponent } = await import(‘echarts/components’);
const { CanvasRenderer } = await import(‘echarts/renderers’);
echarts.use([XXXChart, XXXComponent, CanvasRenderer]);
}
由于歷史需求迭代原因,我們對 Ant-Design-Vue-1.x 進行了二次定制開發,這也導致了ICON全量引入,我們這里使用的方案是重定向到本地文件來進行控制 ,使用 alias 將 @ant-design/icons/lib/dist 指向項目中的 antdIcon.js,然后在 antdIcon.js 文件中按需導出即可,通過按需加載,ICON引入體積從 500K+ 降低到 30K+。
// vue.config.js alias配置
resolve: {alias: {'@ant-design/icons/lib/dist$': path.resolve(\_\_dirname, './src/plugins/antdIcons.js'),}
}// src/plugins/antdIcons.js
export { default as CheckCircleOutline } from '@ant-design/icons/lib/outline/CheckCircleOutline';
export { default as CheckCircleFill } from '@ant-design/icons/lib/fill/CheckCircleFill';
-
檢查刪除冗余依賴,避免重復npm包引入;隨著平臺長期的發展迭代,或多或少都會存在冗余的 mf、npm 資源,同時我們在對微前端子應用的包體積進行分析時,發現子應用通過 npm 引入的Echarts,而主應用本身也引入相同的庫,相對于引入了2 遍 Echarts,這個時候我們改造了子應用的依賴引入方式,通過傳參的方式將Echarts實例傳遞給子應用,避免重復引入和加載相同資源。
//主應用 通過props傳遞依賴
import { start, loadMicroApp, prefetchApps } from ‘qiankun’;export default {
name: ‘MicroWidgetReact’,
methods: {
async loadMicroApp(){
const echarts=awaitthis.echarts = await this.echarts=awaitthis.initEcharts();
this.microApp = loadMicroApp({
name: `xxx`,
props: {
…props,
$echarts: $echarts,
},
});
},
},
};//子應用配置 externals 并且外鏈依賴加上 ignore 屬性(這是自定義的屬性,非標準屬性)
5.3 空間優化 (CPU 占用、內存占用、本地緩存等)
我們在做性能優化的時候,很多情況下都會依賴時間換空間、或者空間換時間等方式,這里需要根據項目的實際情況做出取舍,選擇相對合適的一種方案去進行優化。資源占用常見的優化方式包括:
-
代碼優化:精簡和優化 JavaScript 和 CSS 代碼,避免使用過多的循環和遞歸操作,減少對 CPU 的占用。
-
避免內存泄漏:定期檢查并優化內存使用,避免出現內存泄漏問題,可以使用瀏覽器的開發者工具進行內存分析。
-
圖片懶加載:延遲加載圖片,只有當圖片進入可視區域時再加載,減少內存占用。
-
數據本地存儲:使用瀏覽器提供的本地存儲功能(如LocalStorage或IndexedDB),將一些數據緩存到本地,減少對網絡請求的依賴,提高性能。
-
使用 Web Workers:將一些耗時的任務放到 Web Workers 中執行,減輕主線程的負擔,從而減少 CPU 占用。
-
使用服務端渲染:使用服務端渲染技術,減少客戶端的計算壓力,提高頁面加載速度。
-
使用資源壓縮:對 JavaScript、CSS、圖片等資源進行壓縮,減小文件大小,降低網絡傳輸和內存占用。
本次我們主要使用了 Web Workers 和 數據解綁 (Object.freeze) 等方式進行空間優化,減少了CPU和內存的占用。通過 Web Workers 將需要復雜計算任務放到 Worker 線程,避免阻塞其他首頁渲染任務,釋放主線程資源,實現單線程到多線程。但是這里要注意,過多的使用 Web Workers 有時候反而會導致資源的過度占用,因為 Web Workers 本身也會占用一定的內存資源,而 Workers 之間的通信和數據同步也可能會帶來復雜性和性能開銷,特別是在大規模的并發任務處理時,所以我們需要根據場景合理使用。
六、優化前后對比
整體性能提升 292%:
優化后的加載效果對比:
七、性能監控
為了保證平臺在后續的迭代過程中,持續保持高性能,我們引入Chrome 開源的 web-vitals 庫,結合自研的運行時性能監控埋點(卡頓、崩潰),以及平臺的數據可視化能力,實現對前端整體性能的監控。并利用了平臺的數據監控預警能力,通過對不同指標的配置告警服務,增加性能指標相關的告警,在性能指標發生異動時,及時發現問題,優化性能,保障了用戶使用體驗。
八、總結
上面講了那么多優化方法,都是針對當前項目進行的針對性優化 ,所以我們進行優化時,需要根據具體情況和需求,結合不同的優化策略來達到最佳的性能優化效果。前端性能優化是一個重要的主題,它涉及到許多方面,包括頁面加載速度、交互響應時間、資源利用效率等。但不管什么樣的優化方式,他們的核心思路都是一致的,因為在用戶能看到頁面,并且與之交互之前,都是有固定的步驟的,所以優化的核心思路就是:**盡可能去掉一些關鍵步驟、盡可能提前一些重要步驟、盡可能優化某個具體步驟。**比如 SSR 相比于 CSR,用戶能更快的看到頁面,就是去掉了「下載入口index.html,下載并執行 CSS、JS,請求接口」這幾個關鍵步驟,比如上面說的對高優先級資源進行預加載就是提前一些重要步驟,再比如說通過web workers 避免 JS 執行時產生 Long Task就是優化某個具體步驟。以上就是本次性能優化實踐的所有內容,希望能對你有所幫助。