新時(shí)代的 Google Web Vitals 性能指標(biāo)
作者:Milica Mihajlija
原文:https://calibreapp.com/blog/new-generation-of-performance-metrics
翻譯 / 潤(rùn)色:單是昊
來(lái)源公眾號(hào):ByteDance Web Infra
這是我們 Web Infra APM 團(tuán)隊(duì)體驗(yàn)監(jiān)控相關(guān)系列的第一篇文章,后續(xù)會(huì)持續(xù)更新這個(gè)專題。記得先加個(gè)關(guān)注,不迷路。
傳統(tǒng)的性能指標(biāo)如?load time[1]?或?DOMContentLoaded[2]?專注于容易衡量的技術(shù)細(xì)節(jié),但是它們很難反應(yīng)出用戶所真正關(guān)心的是什么。如果你僅僅是把加載速度優(yōu)化的更快,你很快就會(huì)發(fā)現(xiàn)網(wǎng)站的用戶體驗(yàn)依然很差。一個(gè)站點(diǎn)的總加載時(shí)間可以很快,但如果它直到所有內(nèi)容都準(zhǔn)備好了才渲染的話,用戶只能盯著空白的屏幕一段時(shí)間。如果點(diǎn)擊了按鈕但沒(méi)有反應(yīng),是因?yàn)橹骶€程被 JavaScript 任務(wù)占滿而阻塞了,此時(shí)雖然頁(yè)面已經(jīng)“加載”,但用戶依然會(huì)感到沮喪。

和上面的例子相比,這個(gè)頁(yè)面有更長(zhǎng)的總加載時(shí)間,但是漸進(jìn)式的渲染內(nèi)容并且不用過(guò)度的 JavaScript 來(lái)阻塞主線程。用戶體驗(yàn)會(huì)更好,但加載時(shí)間上卻無(wú)法反映。

這就是創(chuàng)建用戶為中心的性能指標(biāo)的原因,它們專注于用戶視角下的瀏覽體驗(yàn)。
以用戶為中心的性能指標(biāo)衡量頁(yè)面顯示有用內(nèi)容的速度,
用戶是否可以與之交互,以及這些交互是否流暢且無(wú)延遲。
用戶對(duì)頁(yè)面是否“快”的看法,會(huì)受到加載體驗(yàn)中不同時(shí)刻的影響,所以這里有幾個(gè)指標(biāo)試圖捕捉這一點(diǎn):
| 用戶體驗(yàn) | 指標(biāo) |
|---|---|
| 發(fā)生了嗎? | First Paint (FP), First Contentful Paint (FCP) |
| 內(nèi)容有用嗎? | First Meaningful Paint (FMP), Speed Index (SI) |
| 內(nèi)容可用嗎? | Time to Interactive (TTI) |
這些指標(biāo)讓我們離衡量用戶體驗(yàn)又近了一步,但都不是完美的,業(yè)界持續(xù)研究和開(kāi)發(fā)用來(lái)描述良好用戶體驗(yàn)的關(guān)鍵指標(biāo)。隨著對(duì)于用戶體驗(yàn)的理解的深入和測(cè)量能力的增強(qiáng),指標(biāo)也同樣需要隨之進(jìn)化。
這個(gè)過(guò)程的結(jié)果是三個(gè)全新的性能指標(biāo),它們填補(bǔ)了用戶體驗(yàn)故事中的空白。
Largest Contentful Paint (LCP) Total Blocking Time (TBT) Cumulative Layout Shift (CLS)
Largest Contentful Paint
Largest Contentful Paint (LCP) 意味著
最大的內(nèi)容在可視區(qū)域內(nèi)變得可見(jiàn)的時(shí)間點(diǎn)
最大的元素,例如一篇文章中的一大段文字或產(chǎn)品頁(yè)面上的一張圖片,大概就是讓你理解頁(yè)面內(nèi)容的最有用的元素。經(jīng)過(guò)測(cè)試[3],LCP 非常近似于頁(yè)面主要內(nèi)容加載的時(shí)間點(diǎn)。

在上面的例子中,LCP 大概在 0.8ms 也就是香蕉圖片加載完時(shí)發(fā)生。
為了優(yōu)化 LCP,確保:
消除阻塞渲染的資源[4]。 最小化關(guān)鍵請(qǐng)求鏈[5],通過(guò)以正確的優(yōu)先級(jí)和順序加載資源。 壓縮圖像,并為不同的設(shè)備提供不同的圖像大小。 優(yōu)化 CSS,壓縮文件和提取關(guān)鍵 CSS[6]。 使用字體加載策略[7]來(lái)避免了不可見(jiàn)文字的閃爍(FOIT)。
為什么我們需要 ?LCP
瀏覽器和性能監(jiān)控工具已經(jīng)報(bào)告繪制指標(biāo)很長(zhǎng)一段時(shí)間了。我們的目標(biāo)一直是衡量用戶感知頁(yè)面加載進(jìn)度的關(guān)鍵時(shí)刻。然而,其中的一些指標(biāo)據(jù)我們所知,有一些明顯的缺陷:
| 指標(biāo) | 定義 | 問(wèn)題 |
|---|---|---|
| First Contentful Paint (FCP) | 瀏覽器繪制第一塊 DOM 內(nèi)容的時(shí)間點(diǎn)。 | - 通常和用戶無(wú)關(guān)(例如加載指示器或者進(jìn)度條) |
| First Meaningful Paint (FMP) | 頁(yè)面加載中產(chǎn)生最劇烈的布局變化后的繪制時(shí)間點(diǎn)。 | - 非標(biāo)準(zhǔn)化并且難以在瀏覽器之間統(tǒng)一實(shí)現(xiàn)。- 約 20% 的情況下不準(zhǔn)確。" |
| Speed Index (SI) | Speed Index (SI) | - 復(fù)雜的指標(biāo),難以解釋。- 計(jì)算密集,所以在主流瀏覽器中都不可用于真實(shí)用戶監(jiān)控(RUM)。" |
LCP 則不同:
容易理解。 相關(guān)的(給出與SI相似的結(jié)果) 在 RUM 工具中容易計(jì)算和上報(bào)。
事實(shí)上 LCP 的出現(xiàn)并不意味著其他指標(biāo)就是沒(méi)用的。FCP 依然是相關(guān)的,因?yàn)樗o用戶頁(yè)面實(shí)際加載的反饋。關(guān)于 FMP 的實(shí)驗(yàn)則對(duì) LCP 的發(fā)展提供了有價(jià)值的見(jiàn)解。
最大內(nèi)容繪制在?Calibre[8](一個(gè)性能監(jiān)控平臺(tái))、Chrome DevTools 或通過(guò)?Largest Contentful Paint API[9]?都可以使用。在 Lighthouse(從 6.0 版本開(kāi)始)中 LCP 會(huì)被用來(lái)計(jì)算性能得分。如果想要學(xué)習(xí)更多有關(guān)如何計(jì)算得分、和前一個(gè)版本相比有何變化的話,請(qǐng)查看性能得分計(jì)算器[10]。
Total Blocking Time
Total Blocking Time(TBT) 描述了 JavaScript 主線程活動(dòng)。
它有助于理解在加載期間,頁(yè)面無(wú)法響應(yīng)用戶輸入的時(shí)間有多久。
在多數(shù)情況下,所有渲染網(wǎng)頁(yè)[11]的工作、運(yùn)行 JavaScript 和響應(yīng)用戶輸入,都發(fā)生在主線程上。當(dāng)用戶在主線程正在處理其他任務(wù)時(shí)點(diǎn)擊了按鈕,則響應(yīng)將被延遲,直到主線程空閑。如果延遲很小,比如小于 50ms 的話,用戶甚至不會(huì)注意到。如果主線程阻塞更久的話,用戶則會(huì)感受到頁(yè)面的的延遲和未響應(yīng)。
TBT 量化了主線程花費(fèi)在長(zhǎng)任務(wù)[12]上的時(shí)間,以估計(jì)長(zhǎng)任務(wù)在頁(yè)面加載過(guò)程的過(guò)程中潛在的影響用戶交互的風(fēng)險(xiǎn)。
什么是長(zhǎng)任務(wù)?
如果一個(gè)任務(wù)在主線程上運(yùn)行超過(guò) 50 毫秒,那么它就是長(zhǎng)任務(wù)。超過(guò) 50ms 后的任務(wù)耗時(shí),都算作任務(wù)的阻塞時(shí)間。

一個(gè)頁(yè)面的 Total Blocking Time 總阻塞時(shí)間,是從 FCP 到 TTI (Time to Interactive 可交互時(shí)間)之間所有長(zhǎng)任務(wù)的阻塞時(shí)間的總和。

下表是上述例子中,TBT 的計(jì)算方式:
| Task | 任務(wù)時(shí)間 | 任務(wù)阻塞時(shí)間(> 50 ms) |
|---|---|---|
| 1 | 75ms | 25ms |
| 2 | 25ms | 0ms |
| 3 | 85ms | 35ms |
| 4 | 30ms | 0ms |
| Total Blocking Time | 0ms |
跟蹤 TBT 是改善頁(yè)面交互性的第一步。如果你注意到 TBT 值過(guò)高:
對(duì) JavaScript bundle 進(jìn)行代碼分割,并延遲加載那些對(duì)初始加載不重要的包。 可能的話,將代碼分解成工作更少、執(zhí)行更快的函數(shù)。 減少頻繁的 DOM 查詢。 將計(jì)算密集型任務(wù)交給 Service workers 或 Web workers。
Total Blocking Time 和 Time To Interactive 的區(qū)別?
Time to Interactive (TTI)[13]?可交互時(shí)間衡量頁(yè)面何時(shí)可以可靠的響應(yīng)用戶的輸入。如果頁(yè)面的主線程上至少 5 秒都沒(méi)有長(zhǎng)任務(wù),那么可以認(rèn)為它是“完全可交互的”。
Total Blocking Time 總阻塞時(shí)間,只在 FCP 和 TTI 之間計(jì)算。
TTI 識(shí)別主線程何時(shí)變?yōu)榭臻e狀態(tài)。
TBT 量化主線程在空閑之前的繁忙程度。
TTI 有時(shí)可能會(huì)誤導(dǎo)用戶,但當(dāng)與 TBT 結(jié)合使用時(shí),您就會(huì)更清楚地了解頁(yè)面對(duì)用戶輸入的響應(yīng)程度。
例如,兩個(gè)頁(yè)面可以有相同的TTI,但如果其中一個(gè)頁(yè)面將工作分解成更小的任務(wù),而另一個(gè)頁(yè)面的主線程上一直占滿了一個(gè)很長(zhǎng)的任務(wù),那么第二種情況下的用戶體驗(yàn)將會(huì)更差。這種差異將反映為高得多的 TBT,如下圖所示:

在 Calibre 和 Lighthouse 中都有Total Blocking Time 指標(biāo),和LCP一樣,TBT也會(huì)在 Lighthouse 6.0 以上被加入性能分?jǐn)?shù)計(jì)算。
Cumulative Layout Shift
累積布局偏移(CLS)量化了
在頁(yè)面加載期間,視口中有多少元素移動(dòng)。
這里有個(gè)例子,移動(dòng)內(nèi)容(提示元素在頁(yè)面頂部加載,并將頁(yè)面內(nèi)容向下移動(dòng))可能會(huì)讓用戶錯(cuò)過(guò)他們想要點(diǎn)擊的按鈕:
更糟糕的是,這種內(nèi)容偏移可能會(huì)導(dǎo)致用戶點(diǎn)到他們本不想點(diǎn)擊的按鈕。
這里有另一個(gè)例子,頁(yè)面內(nèi)容移動(dòng)幾個(gè)像素會(huì)引起相當(dāng)大的麻煩:

一個(gè)演示布局穩(wěn)定性對(duì)用戶負(fù)面影響的截屏。來(lái)源:web.dev[14]
無(wú)論是以一種增加意外點(diǎn)擊幾率的方式加載廣告,還是在加載新聞圖片時(shí)文本向下移動(dòng),內(nèi)容的意外移動(dòng)都會(huì)讓人非常不舒服。
Cumulative Layout Shift[15]?通過(guò)測(cè)量它對(duì)用戶發(fā)生的頻率,來(lái)幫助您解決這個(gè)問(wèn)題。它引入了用戶體驗(yàn)中一個(gè)全新的類別 —— 可預(yù)測(cè)性。
下面是一些常見(jiàn)的布局不穩(wěn)定問(wèn)題,以及它們的解決方法:
對(duì)于沒(méi)有指定尺寸的圖像,瀏覽器會(huì)先渲染一個(gè) 1x1 像素的占位直到整個(gè)圖片下載完成,一旦圖像渲染,它會(huì)導(dǎo)致布局的其余部分發(fā)生變化。為了避免這個(gè)問(wèn)題,可以為 img 元素添加 width 和 height 屬性[16]。
在渲染內(nèi)容之后異步獲取數(shù)據(jù)然后插入,可能會(huì)導(dǎo)致布局變化。這種情況下,一種比較好的實(shí)踐是用內(nèi)容占位符,這樣真正內(nèi)容加載后布局就不會(huì)產(chǎn)生太大的變化。
廣告通常是異步加載的,在加載時(shí)可能會(huì)取代其他內(nèi)容。如果這導(dǎo)致了意外點(diǎn)擊,那就更令人心煩了,所以最好提前定義廣告空間的尺寸。
利用某些 CSS 屬性做動(dòng)畫(huà)可能會(huì)導(dǎo)致布局變化[17],在大部分情況下,你可以用?
transform?屬性做動(dòng)畫(huà)來(lái)避免它。當(dāng)利用
font-display: swap來(lái)進(jìn)行新舊字體切換時(shí),由于字體之間的大小差異,當(dāng)新字體加載并替換后備字體時(shí),頁(yè)面布局通常會(huì)發(fā)生變化。為避免此問(wèn)題,你可以在?font-style-matche[18]r 調(diào)整不同字體的尺寸,以減小字體切換對(duì)布局的影響。
測(cè)量累積布局偏移
當(dāng)渲染的元素在頁(yè)面加載期間移動(dòng)時(shí),它們會(huì)被標(biāo)記為不穩(wěn)定,并且它們?cè)谙鄬?duì)于視口的移動(dòng)決定了布局偏移分?jǐn)?shù)。CLS 仍然在積極開(kāi)發(fā)中,具體的公式可能會(huì)變,但目前來(lái)說(shuō),布局偏移的分?jǐn)?shù)是由以下因素決定的:
不穩(wěn)定元素移動(dòng)的距離 —— 距離系數(shù)。 受不穩(wěn)定元素影響的區(qū)域面積 —— 影響系數(shù)。

紫色箭頭代表距離系數(shù),藍(lán)色矩形代表影響系數(shù)
在上面的例子中,元素向下移動(dòng)了視口高度的 ?,所以距離系數(shù)是?0.33。
元素在其起始位置和移動(dòng)后的位置所占的面積占視口總面積的 ?,因此影響分?jǐn)?shù)為?0.66。
layout shift score = distance fraction * impact fraction
布局偏移得分為?0.33 × 0.66 = 0.2178。
累積布局偏移分?jǐn)?shù),是所有不穩(wěn)定元素在頁(yè)面加載期間移動(dòng)的分?jǐn)?shù)之和。
CLS分?jǐn)?shù)越低越好,因?yàn)檫@意味著
在頁(yè)面加載過(guò)程中發(fā)生的內(nèi)容的偏移較少。

如果上面例子中的元素只相對(duì)視口移動(dòng)了 10%,那么距離系數(shù)就是?0.1,影響系數(shù)是?0.4。布局偏移得分就僅為?0.1 × 0.4 = 0.04。
一個(gè)理想但不現(xiàn)實(shí)的情況是,CLS 得分為 0。在現(xiàn)實(shí)中的 Chrome 用戶體驗(yàn)報(bào)告中,CLS 分?jǐn)?shù)小于 5 即可被認(rèn)為是理想的。不同類型的網(wǎng)站可能會(huì)有目的的移動(dòng)內(nèi)容[19]。此時(shí)的高 CLS 分?jǐn)?shù)并不意味著糟糕的用戶體驗(yàn)。任何情況下,監(jiān)測(cè) CLS 都有助于發(fā)現(xiàn)預(yù)期之外的布局偏移增長(zhǎng)并且修復(fù)布局不穩(wěn)定的問(wèn)題[20]。
CLS 已經(jīng)在 Chrome 用戶體驗(yàn)報(bào)告中可用,你也可以在 JavaScript 中通過(guò) Layout Instability API 測(cè)量 CLS[21]。Calibre 和 Lighthouse 也會(huì)加入這個(gè)能力。
通過(guò)跟蹤現(xiàn)代 Web 性能指標(biāo)來(lái)提升客戶體驗(yàn)
選用你趁手的性能監(jiān)控武器,自動(dòng)化的性能監(jiān)控是跟蹤優(yōu)化和捕獲回歸問(wèn)題的關(guān)鍵。有了現(xiàn)代 Web 指標(biāo)提供的思路,你可以不斷的完善用戶體驗(yàn),并創(chuàng)造出偉大的產(chǎn)品。
參考資料
load time:?https://developer.mozilla.org/en-US/docs/Web/Events/load
[2]DOMContentLoaded:?https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event
[3]經(jīng)過(guò)測(cè)試:?https://calendar.perfplanet.com/2019/developing-the-largest-contentful-paint-metric/
[4]阻塞渲染的資源:?https://web.dev/render-blocking-resources/
[5]最小化關(guān)鍵請(qǐng)求鏈:?https://calibreapp.com/blog/critical-request
[6]提取關(guān)鍵 CSS:?https://web.dev/extract-critical-css/
[7]字體加載策略:?https://www.zachleat.com/web/comprehensive-webfonts
[8]Calibre:?https://calibreapp.com/
[9]Largest Contentful Paint API:?https://wicg.github.io/largest-contentful-paint/
[10]性能得分計(jì)算器:?https://paulirish.github.io/lh-scorecalc/
[11]渲染網(wǎng)頁(yè):?https://calibreapp.com/blog/investigate-animation-performance-with-devtools#demystifying-rendering
[12]長(zhǎng)任務(wù):?https://web.dev/long-tasks-devtools/#what-are-long-tasks
[13]Time to Interactive (TTI):?https://calibreapp.com/blog/time-to-interactive
[14]web.dev:?https://web.dev/cls/
[15]Cumulative Layout Shift:?https://calibreapp.com/blog/cumulative-layout-shift
[16]可以為 img 元素添加 width 和 height 屬性:?https://www.youtube.com/watch?v=4-d_SoCHeWE&feature=youtu.be
[17]利用某些 CSS 屬性做動(dòng)畫(huà)可能會(huì)導(dǎo)致布局變化:?https://calibreapp.com/blog/investigate-animation-performance-with-devtools#layers-and-compositing
[18]font-style-matche:?https://meowni.ca/font-style-matcher/
[19]可能會(huì)有目的的移動(dòng)內(nèi)容:?https://web.dev/cls/#vs.
[20]修復(fù)布局不穩(wěn)定的問(wèn)題:?https://dev.to/chromiumdev/fixing-layout-instability-176c
[21]在 JavaScript 中通過(guò) Layout Instability API 測(cè)量 CLS:?https://dev.to/chromiumdev/measuring-cumulative-layout-shift-cls-in-webpagetest-5cle
