頁面CLS 最佳化實踐
來源:之家技術
1. CLS 誕生背景
2. CLS 定義
CLS (Cumulative Layout Shift) 累計佈局偏移指標,透過度量“視覺穩定性”反映使用者對頁面佈局偏移所產生的主觀視覺體驗。
Google 定義每隔 5 秒作為一個 CLS 監聽"時間視窗",並且要求在每個"時間視窗"內,相鄰兩次偏移的時間間隔小於 1 秒。在單個"時間視窗"內,CLS 值是由多個元素的偏移值累加得到的。如果頁面中的元素佈局持續發生偏移(持續時間大於 5 秒),則可能會形成多個"時間視窗",每個"時間視窗"的 CLS 值可能不同。在這種情況下,將最大"時間視窗"的 CLS 值作為該頁面的最終 CLS 值。
如下圖:CLS = Max(session window 1,session window 2,session window 3) = 0.105
2.1
如何對 CLS 評級?
谷歌定義頁面 CLS 數值應該控制在 0.1 以內,包括移動和桌面裝置。為了確保大多數使用者達成目標,一個良好的測量閾值為第 75 個百分位數。
3. CLS 如何計算
3.1
不穩定性元素
任何位於可視區域內的可見元素,如果其起始位置在兩幀之間發生變化,則被視為“不穩定元素”。這些“不穩定元素”用於計算佈局偏移。需要注意的是,如果新元素新增到 DOM 或現有元素的尺寸發生改變,只要這些變化不導致其他可見元素的起始位置發生變化,它們就不會被計算為佈局偏移。
3.2
計算公式
佈局偏移分數 = 影響分數(impact fraction) * 距離分數 (distance fraction)
舉例 : 0.07 = 0.5 * 0.14
► 影響分數
該計算因子的含義是測量“不穩定元素”對兩幀之間的可視區域產生的影響,即可視元素的起始位置在兩幀之間發生變化後,兩幀中元素可視區域的合集佔總可視區域的百分比。
如下圖所示:
上圖中黃色塊 P 標籤為“不穩定元素”,其自身元素佔可視區域的 50%,在兩幀中移動了可視區域高度的 25%,紅色虛線表示兩幀中元素的可見區域集合,該集合佔總可視區域的 75%,因此在本例中影響分數為 0.75。
► 距離分數
該計算因子的含義是,測量不穩定元素相對於可視區域在一幀中位移的最大距離(該位移距離可以是水平或者垂直方向,同時存在時取最大者),除以可視區域的最大尺寸維度(尺寸維度可以是寬度或高度,取較大者)。如下圖所示:
上圖中最大可視區域尺寸維度是高度,不穩定元素位移的距離為可視區域高度的 25%,因此距離分數為 0.25 。
根據公式:佈局偏移分數 = 影響分數 * 距離分數,以上圖為例 CLS 值為 0.75(影響分數)* 0.25(距離分數)= 0.1875 。
3.3
舉個例子
如上圖所示,點選 “Click Me!” 按鈕時綠色框部分為不穩定元素,發生了向下的位移,其影響範圍為紅色虛線框部分(底部超出可視區域不做計算),佔總可視區域的 50%,即影響分數為 0.5 。
距離分數由紫色箭頭表示,在高度尺寸維度發生了位移,其位移距離為可視區域高度的 14%,即距離分數為 0.14 。
所以佈局偏移分數是 0.5 * 0.14 = 0.07 。
3.4
符合預期的佈局偏移
佈局偏移並不總是壞事。(佈局偏移只有在使用者並不期望其發生時才算是壞事), 以下兩種情況, 不會影響 CLS 分數。
(1)由使用者發起的佈局偏移
使用者互動(如單擊連結、點選按鈕、在搜尋框中鍵入資訊等), 500毫秒之後發生的 CLS 都會帶有"hadRecentInput"標記, 不會影響 CLS 分數 。
(2)動畫和過渡
動畫和過渡如果做得好,確實是一個在更新頁面內容時不讓使用者感到突兀的好方法。CSS transform 屬性可以幫助我們在不觸釋出局偏移的情況下為元素設定動畫:
用 transform: scale() 來替代和調整 height 和 width 屬性。
如需使元素能夠四處移動,可以用 transform: translate() 來替代和調整 top、right、bottom 或 left 屬性。
4. CLS 測量
在上部分中對 CLS 的基礎理論進行了介紹,並透過相關示例演示了 CLS 值是如何計算的,接下來我們看一下在瀏覽器中如何透過工具對 CLS 進行跟蹤測量。
4.1
DevTools
開啟 Chrome DevTools,在 Performance 標籤選項卡中點選“錄製”按鈕並重新整理頁面,您將得到 CLS 的跟蹤資訊,大部分情況下您可以透過該功能還原定位線上 CLS 問題。
如下圖:
點選不同的“紅色塊”可以檢視其對應的 CLS 值及 DOM元素和相關偏移資訊。如下圖:
4.2
透過 JavaScript 測量 CLS
我們可以使用 JavaScript 和瀏覽器原生的 PerformanceObserver 來測量 CLS,透過監聽 layout-shift 條目,並根據每 5 秒為一個視窗期的最大分數累計規則,對這些監聽到的 layout-shift 條目資料進行計算。
實現程式碼如下:
let clsValue = 0;
let clsEntries = [];
let sessionValue = 0;
let sessionEntries = [];
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// 只將不帶有最近使用者輸入標誌的佈局偏移計算在內。
if (!entry.hadRecentInput) {
const firstSessionEntry = sessionEntries[0];
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
// 如果條目與上一條目的相隔時間小於 1 秒且
// 與會話中第一個條目的相隔時間小於 5 秒,那麼將條目
// 包含在當前會話中。否則,開始一個新會話。
if (sessionValue &&
entry.startTime - lastSessionEntry.startTime < 1000 &&
entry.startTime - firstSessionEntry.startTime < 5000) {
sessionValue += entry.value;
sessionEntries.push(entry);
} else {
sessionValue = entry.value;
sessionEntries = [entry];
}
// 如果當前會話值大於當前 CLS 值,
// 那麼更新 CLS 及其相關條目。
if (sessionValue > clsValue) {
clsValue = sessionValue;
clsEntries = sessionEntries;
// 將更新值(及其條目)記錄在控制檯中。
console.log('CLS:', clsValue, clsEntries)
}
}
}
}).observe({type: 'layout-shift', buffered: true});
4.3
web-vitals Javascript 庫
GoogleChrome 提供了 web-vitals JavaScript 開源庫,用於測量使用者瀏覽器端真實效能,除 CLS 指標外還包括 FID(First Input Delay)、TTFB(Time to First Byte)、FCP(First Contentful Paint)、LCP(Largest Contentful Paint)、INP(Interaction to next Paint)。程式碼示例:
import { onCLS } from 'web-vitals';
onCLS((metric) => {
console.log(metric);
});
5. CLS 採集與上報
透過使用 web-vitals JavaScript 庫,我們可以便捷地獲取頁面的 CLS 和其他效能指標,這在很大程度上簡化了資料測量工作。接下來,為了收集使用者頁面的真實 CLS 效能資料,我們可以設計和開發一套資料採集 sdk,將採集到的資料上報至伺服器端進行儲存。隨後,透過後端大資料分析展示頁面的整體 CLS 效能水平,從而為開發人員提供有針對性的最佳化建議。
5.1
採集、上報時機
在資料採集和上報過程中,我們應確保資料的準確性和完整性,同時也要充分考慮對採集上報時機的設定,這樣可以確保對整體資料統計評分的公平性。
CLS 的"時間視窗"為 5 秒,因此在 sdk 初始化後的第 5 秒,我們將頁面效能資料進行資料採集與上報。然而,sdk 作為一個獨立的 JS 檔案,可以在頁面中透過同步或非同步載入。特別是在非同步載入方式下,如果延遲 5 秒後再進行採集上報,很可能已超過第一個 CLS "時間視窗"。這樣的資料採集可能會影響最終統計評分的公平性,其影響主要體現在以下兩個方面:
► 大於 5 秒上報
受使用者停留頁面的時間因素影響,時間越靠後使用者資料丟失的機率就越大。
如上圖所示,採集工具 js(紅框內) 下載時機在第 3.3 秒開始下載,如果在此基礎上再延遲 5 秒後採集上報資料,真實的採集時間為 8.3 秒,假如使用者停留時長小於 8.3 秒,此情況下若不採取措施該取樣資料將會遺漏掉。
► 小於 5 秒上報
相反,如果將延時時間設定為小於 5 秒(如參考 Sentry@7.64.0 的預設設定時間 1 秒),所得的測量值準確性將得不到保證。為此我們用 web-vitals 做了一次資料分析對比實驗,在日均 pv 約 10 萬左右的網站上持續 3 天對 CLS 資料進行測量,並分別設定在 5 秒和 1 秒後採集和上報。對比結果發現 1 秒上報其各項指標資料丟失率非常大,且資料值偏小,無法保證其準確性。
如下圖所示:
► 最優方案
為了解決 sdk 初始化時間大於或小於第一個 CLS "時間視窗"所產生的影響,我們透過研究 web-vitals 原始碼發現 CLS 以及其他效能指標以 PerformanceNavigationTiming 的 "startTime" 為相對起點。因此,可以將 sdk 初始化時間與該 "startTime" 相減。如果兩者的差值大於 5 秒,則可以立即進行資料採集與上報;反之,可以將距離 5 秒的差值作為延遲等待時間。
PerformanceNavigationTiming 模型如下圖所示:
► 頁面關閉時採集上報
當使用者停留時長不足 5 秒提前關閉頁面時,可能導致資料採集丟失。為了解決這個問題,我們可以透過監聽 visibilitychange、pageHide 事件來採集頁面關閉前的 CLS 資料。這種方案儘可能地確保資料不丟失,但無法保證第一個"時間視窗"的 5 秒準確度。
因此,該方案所採集上報的資料只是儘可能的反映頁面真實取樣 PV,並不參與最終的統計評分。
5.2
採集流程設計
因為 CLS 值的獲取是一個持續進行的過程,在監聽 DOM 佈局偏移過程中,資料物件透過非同步回撥方式傳遞。這個持續動作可能會貫穿整個頁面生命週期。由於獲取到的資料需要上報到伺服器端,不斷的進行資料上報將對後端服務帶來巨大壓力。因此,需要設計一個合理的採集流程以避免上述問題。
採集流程如下圖所示:
1) sdk 初始化時已滿足 5 秒,則立即執行 CLS 資料採集並上報,反之延遲至 5 秒再完成採集上報。
2)使用者停留時長不足 5 秒時頁面關閉,監聽 visibilitychange、pageHide 事件完成採集,並透過 sendBeacon 方式上報。
3) 整個頁面生命週期中只允許一次採集、上報。
5.3
資料上報
在上面流程設計圖中我們設計了兩種上報方式,sdk 初始化後 5 秒上報採用 XmlHttpRequest,頁面隱藏或關閉採用 navigator.sendBeacon。
兩種方式詳細對比見下圖:
5.4
CLS 元素輔助定位
在發現網站頁面的 CLS 值較高時,我們需要使用開發的 sdk 協助開發人員將 CLS 值與其 DOM 元素關聯起來,以便確定頁面中累計偏移較大的元素。Web-vitals v3.0.0 及更高版本中新增了 attribution 除錯功能,能夠將 DOM 元素與 CLS 值關聯並輸出。然而,這導致了包體積增加了 7K,並且輸出內容格式固定,無法實現定製化。
為了定位導致 CLS 值過大的元素,同時防止 js 庫體積變大,我們對 web-vitals 中 onCLS 返回的 layout-shift 條目物件進行自定義解析,並對 DOM 元素的 “domPath”路徑輸出,既幫助開發人員快速定位、修復問題,又縮小了 js 庫體積。
如圖:
6. 最佳化實踐
利用 sdk 我們已收集取到了頁面真實資料,下面透過一個真實案例分享我們是如何進行 CLS 最佳化的。
6.1
頁面結構介紹
為了便於大家對頁面結構有清晰的瞭解,我們在最佳化之前對頁面結構按功能做了如下劃分。
1) 文章內容區(Vue 渲染)。
2) 廣告部分(之家廣告 js 載入)。
3) 頁頭頁尾(公共 js 載入)。
4) 其它(各類第三方 js 庫,如統計、埋點、前端各類元件等)。
如圖所示:
6.2
頁面邏輯說明
本專案採用 SSR + Vue 實現前端頁面渲染,在功能職責上 SSR 僅提供一個空頁面模版,其主要內容的渲染工作由 Vue 承擔,廣告位的渲染部分被包含其中,廣告位渲染鏈路為:
第一步,渲染文章內容部分,由 Vue 繫結資料非同步實現。
第二步,判斷有無廣告位,先由 ajax 非同步請求 isHave 介面,判斷是否存在廣告位,如存在則動態下載廣告位 js 及相關資源(圖片、樣式檔案)。
第三步,渲染廣告位, 廣告 js 及資原始檔下載完成後,動態插入 DOM 並完成渲染。
6.3
頁面問題說明
Vue 渲染在廣告位 js 及相關資源的載入之後,且廣告位資源的下載需要等待 isHave 非同步請求返回後,導致廣告內容的渲染過程鏈路被拉長。其次廣告位內容尺寸未知,因此頁面在渲染過程中頁面會因廣告位的動態載入渲染出現一次“抖動”現象,最終導致頁面的 CLS 值過大。
下圖動畫演示了廣告出現的整個渲染鏈路:
6.4
最佳化措施及內容
6.5
資料評分效果
7. 總結
https://web.dev/optimize-cls/
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024924/viewspace-2992688/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- webpack多頁面實踐Web
- Vue頁面骨架屏注入實踐Vue
- Vue 頁面骨架屏注入實踐Vue
- Laravel頁面靜態化最佳實踐Laravel
- Vue單頁及多頁應用全域性配置404頁面實踐Vue
- 前端黑科技:美團網頁首幀最佳化實踐前端網頁
- 頁面壞UV效能最佳化
- 從頁面載入到資料請求,前端頁面效能優化實踐分享前端優化
- 心遇iOS端會話頁效能最佳化 — ReactiveObjC實踐篇iOS會話ReactOBJ
- IdleHandler,頁面啟動最佳化神器
- 移動Web單頁應用開發實踐——頁面結構化Web
- 千萬級資料深分頁查詢SQL效能最佳化實踐SQL
- vue-cli多頁面應用實踐,實現元件預覽Vue元件
- Golang效能最佳化實踐Golang
- .NET雲原生應用實踐(五):使用Blazor WebAssembly實現前端頁面BlazorWeb前端
- Android自動化頁面測速在美團的實踐Android
- React開發管理後臺實踐3---新增新頁面React
- Android+Chrome 真機除錯H5頁面實踐AndroidChrome除錯H5
- 前端面試題 — 前端頁面效能最佳化前端面試題
- 實現不同頁面不同頁首
- 【效能優化實踐】優化打包策略提升頁面載入速度優化
- 如何“拼”出一個頁面-遊戲中心模組化實踐遊戲
- 得物面試:MySQL 深度分頁如何最佳化?面試MySql
- Laravel Web 實戰入門每章課程總結 - 4.最佳化頁面LaravelWeb
- HarmonyOS:應用效能最佳化實踐
- 打包最佳化實踐(如何Code Spliting)
- MySQL8.0效能最佳化(實踐)MySql
- Vue 全家桶仿原生App切換效果和頁面快取實踐VueAPP快取
- QQ音樂Android客戶端Web頁面通用效能優化實踐Android客戶端Web優化
- 實踐指南-網頁生成PDF網頁
- 前端效能最佳化實踐方向與方法前端
- Hadoop YARN:排程效能最佳化實踐HadoopYarn
- 影片生產大映象最佳化實踐
- Django實踐(二)——使用模型類定義資料表,實現表單頁面跳轉Django模型
- 前端頁面水印生成實現前端
- web頁面錄屏實現Web
- HTML頁面轉換為Sharepoint母版頁(實戰)HTML
- Mysql事務原理與最佳化最佳實踐MySql