作者:九思
你好,我是九思,來自騰訊前端技術部,擅長前端監控、工程化相關技術。此篇文章將圍繞前端效能優化中監控問題展開討論。
首先我們要知道,頁面開啟快不快,不是在電腦或手機上的開啟速度說了算,也不是測試同學測試的結果說了算,而是真實使用者使用的時候說了算。
那麼如何去監控使用者真實使用時的頁面效能呢?本文將做細緻介紹(建議收藏)
前端監控需要注意什麼?
首屏
頁面上線後,我們最關心使用者開啟頁面的速度,通常就是首屏。
靜態資源
頁面載入離不開靜態資源的載入,包括 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,只要在 JS 中 new 出 Image 物件就能發起請求,而且還沒有阻塞問題,在沒有js的瀏覽器環境中也能通過 img 標籤正常打點,這是其他型別的資源請求所做不到的。
在所有圖片中1px x 1px 大小,gif 體積最小,相較 BMP/PNG,可以節約41%/35%的網路資源,所以適用 gif 相對是最佳選擇。
限流
頁面的效能資料,每次訪問都會有,如果你的專案 pv 有一定量級,那麼處理起來就會相當耗費資源,而且這些資料我們最終是求平均值或者分位值,所以沒必要全量上報。那麼我們可以在上報前做一些限流處理。
更多內容
除了以上這幾點,前端監控的實操中我們還應該考慮到如何處理這些效能資料,主要有取中位數、平均值、分位數等做法,由於篇幅有限此處不做詳細介紹。
最後,如果這篇文章給你帶來些許有價值的理解,歡迎點贊、分享,更多內容可翻閱我的付費專欄《前端效能優化12問》。