面試必問:前端頁面性能指標(biāo)基本介紹
1. 基本指標(biāo)介紹
首先前端性能指標(biāo)一般分為以下幾種:
- 首屏繪制(First Paint,F(xiàn)P)
- 首屏內(nèi)容繪制(First Contentful Paint,F(xiàn)CP)
- 可交互時間(Time to Interactive,TTI)
- 最大內(nèi)容繪制(Largest Contentful Paint,LCP)
- 首次有效繪制(First Meaning Paint, FMP)
FP 是時間線上的第一個“時間點(diǎn)”,是指瀏覽器從響應(yīng)用戶輸入網(wǎng)址地址,到瀏覽器開始顯示內(nèi)容的時間,簡而言之就是瀏覽器第一次發(fā)生變化的時間。
FCP(全稱“First Contentful Paint”,翻譯為“首次內(nèi)容繪制”),是指瀏覽器從響應(yīng)用戶輸入網(wǎng)絡(luò)地址,在頁面首次繪制文本,圖片(包括背景圖)、非白色的 canvas 或者SVG 才算做 FCP,有些文章說 FCP 是首屏渲染事件,這其實(shí)是不對的。
TTI,翻譯為“可交互時間”表示網(wǎng)頁第一次完全達(dá)到可交互狀態(tài)的時間點(diǎn)。可交互狀態(tài)指的是頁面上的 UI 組件是可以交互的(可以響應(yīng)按鈕的點(diǎn)擊或在文本框輸入文字等),不僅如此,此時主線程已經(jīng)達(dá)到“流暢”的程度,主線程的任務(wù)均不超過50毫秒。在一般的管理系統(tǒng)中,TTI 是一個很重要的指標(biāo)。
FMP(全稱“First Meaningful Paint”,翻譯為“首次有效繪制”表示頁面的“主要內(nèi)容”開始出現(xiàn)在屏幕上的時間點(diǎn),它以前是我們測量用戶加載體驗(yàn)的主要指標(biāo)。本質(zhì)上是通過一個算法來猜測某個時間點(diǎn)可能是 FMP,但是最好的情況也只有77%的準(zhǔn)確率,在lighthouse6.0 的時候廢棄掉了這個指標(biāo),取而代之的是 LCP 這個指標(biāo)。
LCP(全稱“Largest Contentful Paint”)表示可視區(qū)“內(nèi)容”最大的可見元素開始出現(xiàn)在屏幕上的時間點(diǎn)。
2. performance介紹
performance 對象是專門用來用于性能監(jiān)控的對象,內(nèi)置了一些前端需要的性能參數(shù)。
2.1. performance.now()方法
performance.now()返回performance.navigationStart至當(dāng)前的毫秒數(shù)。performance.navigationStart是下文將介紹到的可以說是瀏覽器訪問最初的時間測量點(diǎn)。
2.2. performance.timing
2.3. performance.getEntries()方法
瀏覽器獲取網(wǎng)頁時,會對網(wǎng)頁中每一個對象(腳本文件、樣式表、圖片文件等等)發(fā)出一個 HTTP 請求。performance.getEntries() 方法以數(shù)組形式,返回一個 PerformanceEntry 列表,這些請求的時間統(tǒng)計(jì)信息,有多少個請求,返回?cái)?shù)組就會有多少個成員。
name :資源名稱,是資源的絕對路徑或調(diào)用mark方法自定義的名稱 startTime :開始時間 duration :加載時間 entryType :資源類型,entryType 類型不同數(shù)組中的對象結(jié)構(gòu)也不同!具體見下 initiatorType :誰發(fā)起的請求,具體見下:
值 |
描述 |
mark | 通過 mark() 方法添加到數(shù)組中的對象 |
paint | 通過 measure() 方法添加到數(shù)組中的對象 |
measure |
first-contentful-paint 首次內(nèi)容繪制 |
resource | 所有資源加載時間,用處最多 |
3. 指標(biāo)計(jì)算方法
3.1. 首屏和白屏
關(guān)于首屏和白屏的計(jì)算時間不同的說法比較多但大致相同,主要爭論是關(guān)于首屏圖片是否算首屏加載時間。
白屏:
白屏?xí)r間(First Paint):是指瀏覽器從響應(yīng)用戶輸入網(wǎng)址地址,到瀏覽器開始顯示內(nèi)容的時間,一種比較簡單的做法是在 body 標(biāo)簽之前獲取當(dāng)前時間 - performance.timing.navigationStart,或者直接獲取 performance 中關(guān)于 paint 的兩個數(shù)據(jù),都可以直接作為白屏數(shù)據(jù),這兩個數(shù)據(jù)一般差別不大。
首次繪制 FP 包括了任何用戶自定義的背景繪制,它是首先將像素繪制到屏幕的時刻。
首次內(nèi)容繪制 FCP 是瀏覽器將第一個 DOM 渲染到屏幕的時間。該指標(biāo)報(bào)告了瀏覽器首次呈現(xiàn)任何文本、圖像、畫布或者 SVG 的時間。
也可以使用其他的計(jì)算方法:白屏?xí)r間 = 頁面開始展示的時間點(diǎn) - 開始請求的時間點(diǎn)。
首屏:
首屏?xí)r間:是指瀏覽器從響應(yīng)用戶輸入網(wǎng)絡(luò)地址,到首屏內(nèi)容渲染完成的時間,在需要展示的元素頁面之前獲取當(dāng)前時間 - performance.timing.navigationStart。
首屏?xí)r間 = 首屏內(nèi)容渲染結(jié)束時間點(diǎn) - 開始請求的時間點(diǎn) 首屏計(jì)算的方法比較多,很多文章中使用的方法都不太一樣 performance.timing.interactive - performance.timing.fetchStart 也有一些使用的是 performance.timing.loadEventEnd - performance.timing.navigationStart 不過時間差別應(yīng)該不大,都是用從dom加載完畢減去請求開始或者刷新url的時間。
3.2. TTI
關(guān)于 TTI 可以首先了解下谷歌提出的性能模型 RAIL:
1.響應(yīng):輸入延遲時間(從點(diǎn)按到繪制)小于 100 毫秒。用戶點(diǎn)按按鈕(例如打開導(dǎo)航)。
2.動畫:每個幀的工作(從 JS 到繪制)完成時間小于 16 毫秒。用戶滾動頁面,拖動手指(例如,打開菜單)或看到動畫。拖動時,應(yīng)用的響應(yīng)與手指位置有關(guān)(例如,拉動刷新、滑動輪播)。此指標(biāo)僅適用于拖動的持續(xù)階段,不適用于開始階段。
3.空閑:主線程 JS 工作分成不大于 50 毫秒的塊。用戶沒有與頁面交互,但主線程應(yīng)足夠用于處理下一個用戶輸入。
4.加載:頁面可以在 1000 毫秒內(nèi)就緒。用戶加載頁面并看到關(guān)鍵路徑內(nèi)容。
我們可以通過domContentLoadedEventEnd來粗略的進(jìn)行估算:
TTI:domContentLoadedEventEnd - navigationStart
谷歌實(shí)驗(yàn)室也提供了更加便捷準(zhǔn)確的api包進(jìn)行測算 tti-polyfil:
- import ttiPolyfill from './path/to/tti-polyfill.js';
- ttiPolyfill.getFirstConsistentlyInteractive(opts).then((tti) => {
- // Use `tti` value in some way.
- });
3.3. LCP
在過去,我們也有推薦的性能指標(biāo),如:FMP (First Meaningful Paint)和SI (Speed Index)可以幫我們捕獲更多的首次渲染之后的加載性能,但這些過于復(fù)雜,而且很難解釋,也經(jīng)常出錯,沒辦法確定主要內(nèi)容什么時候加載完。
根據(jù) W3C Web 性能工作組的討論和 Google 的研究,發(fā)現(xiàn)度量頁面主要內(nèi)容的可見時間有一種更精準(zhǔn)且簡單的方法是查看 “繪制面積” 最大的元素何時開始渲染。
所謂繪制面積可以理解為每個元素在屏幕上的 “占地面積” ,如果元素延伸到屏幕外,或者元素被裁切了一部分,被裁切的部分不算入在內(nèi),只有真正顯示在屏幕里的才算數(shù)。圖片元素的面積計(jì)算方式稍微有點(diǎn)不同,因?yàn)榭梢酝ㄟ^ CSS 將圖片擴(kuò)大或縮小顯示,也就是說,圖片有兩個面積:“渲染面積”與“真實(shí)面積”。在 LCP 的計(jì)算中,圖片的繪制面積將獲取較小的數(shù)值。例如:當(dāng)“渲染面積”小于“真實(shí)面積”時,“繪制面積”為“渲染面積”,反之亦然。
頁面在加載過程中,是線性的,元素是一個一個渲染到屏幕上的,而不是一瞬間全渲染到屏幕上,所以“渲染面積”最大的元素隨時在發(fā)生變化。如果使用 PerformanceObserver 去捕獲 LCP,會發(fā)現(xiàn)每當(dāng)出現(xiàn)“渲染面積”更大的元素,就會捕獲出一條新的性能條目。
如果元素被刪除,LCP 算法將不再考慮該元素,如果被刪除的元素剛好是 “繪制面積” 最大的元素,則使用新的 “繪制面積” 最大的元素創(chuàng)建一個新的性能條目。
該過程將持續(xù)到用戶第一次滾動頁面或第一次用戶輸入(鼠標(biāo)點(diǎn)擊,鍵盤按鍵等),也就是說,一旦用戶與頁面開始產(chǎn)生交互,則停止報(bào)告新的性能條目。
上面兩張圖都是在頁面加載過程中,最大元素發(fā)生變化。第一張圖,新的內(nèi)容被加入到DOM,而且這個元素成為了最大元素。第二張圖,布局發(fā)生了變化,之前在視窗中的元素被移出了視窗外。
可以直接使用 PerformanceObserver 來捕獲 LCP:
- const observer = new PerformanceObserver((entryList) => {
- const entries = entryList.getEntries();
- const lastEntry = entries[entries.length - 1];
- const lcp = lastEntry.renderTime || lastEntry.loadTime;
- console.log('LCP:', lcp);
- });
- observer.observe({ entryTypes: ['largest-contentful-paint'] });
LCP也不是完美的,也很容易出錯,它會在用戶進(jìn)行交互后就停止捕獲,可能會獲取到錯誤的結(jié)果,如果有占據(jù)頁面很大的輪播圖也會產(chǎn)生問題會不斷的更新 LCP。
LCP也有現(xiàn)成的計(jì)算工具庫 web-vitals:
- import {getLCP} from 'web-vitals';
- // Measure and log the current LCP value,
- // any time it's ready to be reported.
- getLCP(console.log);