挑戰月薪30K | 前端效能優化的12 條建議(乾貨收藏)

思否程式設計 發表於 2020-12-22
作者:九思

你好,我是九思,來自騰訊前端技術部,擅長前端監控、工程化相關技術。此篇文章將圍繞前端效能優化中監控問題展開討論。

首先我們要知道,頁面開啟快不快,不是在電腦或手機上的開啟速度說了算,也不是測試同學測試的結果說了算,而是真實使用者使用的時候說了算。

那麼如何去監控使用者真實使用時的頁面效能呢?本文將做細緻介紹(建議收藏)

前端監控需要注意什麼?


首屏

頁面上線後,我們最關心使用者開啟頁面的速度,通常就是首屏。

靜態資源

頁面載入離不開靜態資源的載入,包括 js、css、img、video、font 等,在如今盛行 SPA 的場景尤為重要,比如活動頁面會有很多圖片,我們通常會開發一些模板,由產品/運營同學來配置,而圖片多大合適是比較難確定的,網速越來越快,大家對清晰度要求也越來越高,此時就可以通過監測這些圖片的載入速度來酌情優化。

API 請求

資料是頁面中相當重要的元素,可以說沒有資料,你的頁面幾乎沒有使用價值(純靜態除外)。當然這裡我們只能粗暴的監控整個請求的總時間,純前端無法監控各個階段時間,但這對於線上應用也很重要。

其他測速

實際上線後,不同的應用可能會有不同的測速訴求,比如:視訊從載入到播放的時間,此時可以自定義一些測速點,利用之前講述的打點方式來上報。

如何監控靜態資源?


這塊其實還是比較簡單的,只需要利用 PerformanceResourceTiming 即可,並且它的相容性極高,可以覆蓋到幾乎所有場景。

圖片

實際監控時,可以分兩種場景,如果支援 performanceObserver 可以實時監聽,否則使用定時器方式,此外需要將此程式碼放到頁面最頂層,否則無法監控到這段程式碼之前的資源載入。

 const typeList = \['script', 'link', 'img'\]; //   
 const staticTime = {};  
 const dealTime = (entries) => {  
 for (let i = 0, l = entries.length; i < l; i++) {  
 const entry = entries\[i\];  
 if (typeList.indexOf(entry.initiatorType) !== -1) {  
 staticTime\[entry.name\] = entry.connectEnd - entry.connectStart;  
 }  
 }  
 }  
 if (typeof window.PerformanceObserver === 'function') {   
 const observer = new window.PerformanceObserver((list) => {  
 dealTime(list.getEntries());  
 });  
 observer.observe({ entryTypes: \['resource'\] });  
 } else {  
 setInterval(() => {  
 const allEntries = performance.getEntriesByType('resource');  
 const entries = allEntries.slice(allEntries.length);  
 dealTime(entries);  
 }, 5000);  
 }

entry 部分數值

connectEnd: 32.63499999593478

connectStart: 32.63499999593478

decodedBodySize: 160302

domainLookupEnd: 32.63499999593478

domainLookupStart: 32.63499999593478

duration: 37.54000000481028

encodedBodySize: 23876

entryType: "resource"

fetchStart: 32.63499999593478

initiatorType: "link"

name: "https://stackpath.bootstrapcdn.com

nextHopProtocol: "h2"

redirectEnd: 0

redirectStart: 0

requestStart: 44.60999999719206

responseEnd: 70.17500000074506

responseStart: 65.40999999560881

secu reConnectionStart: 32.63499999593478

serverTiming:[]

startTime: 32.63499999593478

transferSize: 23971

workerStart: 0

圖片

API 監控


關於 API 的監控,可以採用重寫 XHR 或者 fetch,這樣可以適用任何的框架、請求庫。時間計算則是通過打點的方式,如果只是固定專案監控,也可以直接採用打點的方式。如果採用重寫的方式,不僅可以監控請求的時長,還可以監控請求的成功失敗率。

打點最簡單的方式:

const startTime = Date.now();  
fn() // 假設這裡是同步執行  
const timeCycle = Date.now() - startTime; // fn的執行耗時

除了以上這幾點,前端監控的實操中我們還應該考慮到上報、限流以及如何處理這些效能資料。

圖片

上報


傳送請求

傳送請求我們很容易想到適用 fetch / XHR,當然也可以使用自動發起請求的 HTML 標籤,比如 script、link、img。上報資料雖然可以拿來分析頁面的真實執行資料,但有一點要注意的是:不能影響當前頁面的執行或最小程度的影響。

是否可以直接用 fetch/XHR 呢?答案是否定的,因為上報的域名和頁面的域名基本是不同的,所以這裡需要可以前端跨域的方式。

說到跨域,瀏覽器的 src 屬性標籤基本都可以,到底用哪個呢?原則上要適用對頁面影響最小的那個,諸如 Script、link 這些標籤之前有講述,他們都會對頁面的執行造成影響。

img 變成了較為合適的方式,構造圖片打點不僅不用插入DOM,只要在 JSnewImage 物件就能發起請求,而且還沒有阻塞問題,在沒有js的瀏覽器環境中也能通過 img 標籤正常打點,這是其他型別的資源請求所做不到的。

在所有圖片中1px x 1px 大小,gif 體積最小,相較 BMP/PNG,可以節約41%/35%的網路資源,所以適用 gif 相對是最佳選擇。

限流

頁面的效能資料,每次訪問都會有,如果你的專案 pv 有一定量級,那麼處理起來就會相當耗費資源,而且這些資料我們最終是求平均值或者分位值,所以沒必要全量上報。那麼我們可以在上報前做一些限流處理。

更多內容


除了以上這幾點,前端監控的實操中我們還應該考慮到如何處理這些效能資料,主要有取中位數、平均值、分位數等做法,由於篇幅有限此處不做詳細介紹。

最後,如果這篇文章給你帶來些許有價值的理解,歡迎點贊、分享,更多內容可翻閱我的付費專欄《前端效能優化12問》。

圖片