上篇文章最後提到了我們可以通過performance的一些屬性對效能做統計,我們會發現performance
物件下有非常多的屬性,遠不止上篇文章提到的DOMContentLoaded
與Load
這兩個事件。
或許你在瀏覽器控制檯見過它們這些身影:DCL
、LCP
、FP
、FCP
、L
這裡的DCL
與L
就是我們上篇文章介紹的DOMContentLoaded
與Load
這兩個事件,那剩下的LCP
、FP
、FCP
又分別代表什麼呢?
在早期前端三劍客的時代或者現在的伺服器端渲染,DCL
與L
確實可以很好地衡量首屏內容展示時間,但對於現代各種框架盛行的單頁應用,由於都是通過JS操作DOM向頁面新增主要內容,DCL
和L
事件就不能很好地衡量首屏顯示時間了。
於是有FP、FCP、FMP被提出來,它們關注的不是載入,而是渲染,因此能更好地表現使用者看到的情況。
FP、FCP這兩個指標雖然表達了渲染的事件,但對「使用者關注的內容」沒有體現,比如首屏渲染出來一個背景,或者一個loading,可能對於使用者來說和白屏區別不大。FMP雖然體現了「關鍵內容」的要素,但它是複雜的、模糊的,甚至是錯誤的,並不能準確識別頁面主要內容的載入時機。
後來LCP指標被提出來,表示「用於度量視口中最大的內容元素何時可見」,它用來代替FMP,表徵頁面的關鍵元素何時可以被使用者看到。
除了載入效能,還有可互動時間、穩定性指標、流暢性指標,在不同的業務場景都可以被監控用來作為提升使用者體驗的依據。
上面我們提到了各種效能相關的事件,那麼它們各種代表的含義是什麼呢?
名詞 | 全稱 | 解釋 |
---|---|---|
FP | firstPaint | 首次繪製 |
FCP | firstContentfulPaint | 首次內容繪製 |
LCP | largestContentfulPaint | 最大內容繪製 |
DCL | domContentLoaded | dom內容解析完成 |
L | loaded | 頁面的load事件 |
當初始的 HTML 檔案被完全載入和解析完成之後,
DOMContentLoaded
事件被觸發,而無需等待樣式表、影象和子框架的完全載入。
document.addEventListener('DOMContentLoaded', (event) => {
console.log('DOM 完全載入以及解析')
});
performance.timing.domContentLoadedEventStart - performance.timing.fetchStart
load
事件在整個頁面及所有依賴資源如樣式表和圖片都已完成載入時觸發。它與DOMContentLoaded
不同,後者只要頁面 DOM 載入完成就觸發,無需等待依賴資源的載入。
window.addEventListener('load', (event) => {
console.log('頁面載入完成');
});
performance.timing.loadEventEnd - performance.timing.fetchStart
很多人可能會誤以為Load
的觸發一定會在DCL
之後,雖然絕大多數我們看到的確實是這樣,但你從兩者的MDN解釋上來看,DCL
關注的時HTML檔案的載入與解析,而Load
只關注資源的載入,所以兩者觸發的先後順序並不是絕對的。
比如下面兩種情況:
第一種: 頁面非常簡單,沒有引入任何外部資源
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="container">
</div>
</body>
</html>
從圖中可以看到此時的Load
觸發在DCL
之前,這是因為load
不包含對檔案解析的時間
第二種: 我們引入一張圖片
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="container">
<img src="https://imgservices-1252317822.image.myqcloud.com/coco/s06012023/9ac85415.g0q5wz.png" class="zan_icon" />
</div>
</body>
</html>
從這張圖上我們可以看到此時DCL
比Load
先觸發,並且很明顯可以看到在兩者之前多了個圖片的下載過程。
所以兩者觸發的先後順序並不是固定的,如果頁面有許多外部資源需要載入,那麼load
事件會後觸發,如果頁面內容較多,外部資源較少,那麼load
事件可能先觸發。
為了應對現在框架盛行的單頁應用新增了下面這幾個指標,它們關注的不再是頁面的載入過程,而是頁面的渲染過程。
為了方便,這兩個放一起講:
FP,全稱 First Paint, 代表首次渲染的時間點,即首次視覺變化發生的時間點。前端開發者經常談到的白屏時間(使用者看不到任何內容)就是使用者存取網頁到 FP 的這段時間。
FCP,全稱 First Contentful Paint,代表首次 DOM 內容 渲染的時間點,DOM 內容 可以是文字、影象(包括背景影象)、
<svg>
元素或非白色的<canvas>
元素。
簡單點理解就是FCP
事件指渲染出第一個內容的事件,而FP
指渲染出第一個畫素點,渲染出的東西可能是內容,也可能不是。
⚠️需要注意的是,FCP
一定不會比FP
晚觸發,但可能會一起觸發,絕大多數情況是FP在FCP之前觸發!
第一種: 無FP
是不是很奇怪,怎麼會有這種情況?其實我們上面DCL
與Load
那裡的第一個案例就出現了這種情況
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="container">
</div>
</body>
</html>
頁面上有節點但沒有樣式,很顯然這種情況是不需要渲染頁面的,所以也就沒有FP
當然這裡需要注意的是這裡的節點不包括一些自身可見的節點(比如img、input、video等)
還有一種情況就是如果要渲染的內容在視口之外,那麼也不會觸發 FP
第二種: 有FP無FCP
同時為了驗證第一種說法的注意點,這裡我就寫一個input
來試試
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="container">
<input type="text" />
</div>
</body>
</html>
從這裡我們就能發現,此時頁面只會觸發FP
,因為沒有內容
// FP
const fp = performance.getEntries('paint').filter(entry => entry.name == 'first-paint')[0].startTime;
// FCP
const fcp = performance.getEntries('paint').filter(entry => entry.name == 'first-contentful-paint')[0].startTime;
瀏覽器不一定等到所有的DOM都解析完再開始渲染,如果DOM節點少,瀏覽器會載入完再渲染,但是如果節點很多,瀏覽器解析一部分節點後就會開始渲染(這時候就會觸發FP)。也就是說,當需要渲染的節點數少的時候,DCL會在FP前面;當需要渲染的節點數很多時候,DCL會在FP後面。
現在來說,絕大部分的專案都是FP
在DCL
之前觸發,這樣使用者可以更快的看到頁面內容。
LCP,全稱 Largest Contentful Paint,根據頁面首次開始載入的時間點(即 first started loading,可以通過
performance.timeOrigin
得到)來報告可視區域內可見的最大影象或文字塊完成渲染的相對時間
為了提供良好的使用者體驗,我們應努力將最大內容繪製時間控制在2.5 秒或更短。
<img>
元素<image>
、 <svg>
元素內的元素<video>
帶有海報影象的元素(使用海報影象載入時間)url()
(而不是CSS 漸變)隨著更多研究的進行,未來可能會新增其他元素
這裡可以通過performance面板,在Timings這一行找到LCP
,點選它再找到下面的summary,就能找到LCP
對應的節點元素了。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
谷歌一直十分重視網站的使用者體驗,移動友好性,頁面載入速度和HTTPS是Google已經使用的頁面排名因素,而2020年,谷歌將Core Web Vitals新納入的使用者體驗指標。
TTI 全稱Time to Interactive 它用於測量頁面從開始載入到主要子資源完成渲染,並能夠快速、可靠地響應使用者輸入所需的時間。
測量TTI一般需要按以下步驟:
為了方便理解,可以對照這張圖
為了提供良好的使用者體驗,網站在普通移動硬體上進行測試時,應該努力將可互動時間控制在5 秒以內。
TBT全稱 Total Blocking Time, 它主要是用於度量 FCP 和 TTI 之間的總的阻塞時間。
只要存在long task(在主執行緒上執行超過 50 毫秒 (ms) 的任務),主執行緒就會被視為「阻塞」。我們說主執行緒被「阻塞」,因為瀏覽器無法中斷正在進行的任務。因此,如果使用者在長時間任務中確實與頁面進行互動,則瀏覽器必須等待任務完成才能響應*。*
測量TBT可以使用谷歌Lighthouse
為了提供良好的使用者體驗,在平均移動硬體上進行測試時,網站應努力使總阻塞時間低於200 毫秒。
CLS 全稱 Cumulative Layout Shift 累積佈局偏移 ,它用來測量整個頁面生命週期內發生的所有意外佈局偏移中最大一連串的佈局偏移分數
只要可視區域中可見元素的起始位置(例如,元素在預設書寫模式下的頂部和左側位置)在兩幀之間發生了變更,該 API 就會報告layout-shift
條目。這樣的元素被視為不穩定元素。
請注意,只有當現有元素的起始位置發生變更時才算作佈局偏移。如果將新元素新增到 DOM 或是現有元素更改大小,則不算作佈局偏移,前提是元素的變更不會導致其他可見元素的起始位置發生改變。
瀏覽器在計算佈局偏移分數時,會檢視可視區域大小和兩個已渲染幀之間的可視區域中不穩定元素的位移。佈局偏移分數是該位移的兩個度量的乘積:影響分數和距離分數(兩者定義如下)。
佈局偏移分數 = 影響分數 * 距離分數
為了提供良好的使用者體驗,網站應該努力將 CLS 分數控制在0.1 或以下。為了確保您能夠在大部分使用者的存取期間達成建議目標值,一個良好的測量閾值為頁面載入的第 75 個百分位數,且該閾值同時適用於移動和桌面裝置。
這裡需要注意的是在寫動畫時優先考慮使用CSS transform
屬性,因為它能夠在不觸發佈局偏移的情況下為元素設定動畫:
transform: scale()
來替代和調整height
和width
屬性。transform: translate()
來替代和調整top
、right
、bottom
或left
屬性。為了更準確的統計效能資料,可以使用web-vitals
庫來測量各項效能指標
可以獲取的指標有:CLS、FID、LCP、以及 FCP、TTFB
import {getCLS, getFID, getLCP} from 'web-vitals'
getCLS(console.log);
getFID(console.log);
getLCP(console.log);
如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖
第一時間獲取最新文章~
-------------------------------------------
如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖 第一時間獲取最新的文章~
掃描下方二維條碼關注公眾號,回覆進群,拉你進前端學習交流群