逐步說明如何使用 WebPageTest 找出並修正版面配置不穩定問題。
我在先前的文章中,介紹了如何使用 WebPageTest 評估累計版面配置位移 (CLS)。CLS 是所有版面配置位移的匯總,因此在本文中,我想深入探討並檢查網頁上的每個版面配置位移,試著瞭解可能導致不穩定的原因,並實際修正問題。
測量版面配置位移
使用 Layout Instability API,我們可以取得網頁上所有版面配置位移事件的清單:
new Promise(resolve => {
new PerformanceObserver(list => {
resolve(list.getEntries().filter(entry => !entry.hadRecentInput));
}).observe({type: "layout-shift", buffered: true});
}).then(console.log);
這會產生未在輸入事件前發生的版面配置位移陣列:
[
{
"name": "",
"entryType": "layout-shift",
"startTime": 210.78500000294298,
"duration": 0,
"value": 0.0001045969445437389,
"hadRecentInput": false,
"lastInputTime": 0
}
]
在本例中,210 毫秒時只有 0.01% 的極小幅度變化。
瞭解變動的時間和嚴重程度,有助於縮小變動原因的範圍。讓我們回到 WebPageTest 實驗室環境,進行更多測試。
在 WebPageTest 中測量版面配置位移
與在 WebPageTest 中評估 CLS 類似,評估個別版面配置位移需要使用自訂指標。幸好,Chrome 77 穩定版推出後,這個程序變得簡單許多。版面配置不穩定性 API 預設為啟用,因此您應該可以在 Chrome 77 中的任何網站上執行該 JS 片段,並立即取得結果。在 WebPageTest 中,您可以使用預設的 Chrome 瀏覽器,不必擔心指令列標記或使用 Canary。
因此,請修改該指令碼,為 WebPageTest 產生自訂指標:
[LayoutShifts]
return new Promise(resolve => {
new PerformanceObserver(list => {
resolve(JSON.stringify(list.getEntries().filter(entry => !entry.hadRecentInput)));
}).observe({type: "layout-shift", buffered: true});
});
這個指令碼中的 Promise 會解析為陣列的 JSON 表示法,而非陣列本身。這是因為自訂指標只能產生字串或數字等基本資料型別。
我將使用 ismyhostfastyet.com 進行測試,這個網站是我用來比較網頁主機實際載入效能的工具。
找出版面配置不穩定的原因
在結果中,我們可以看到 LayoutShifts 自訂指標具有下列值:
[
{
"name": "",
"entryType": "layout-shift",
"startTime": 3087.2349999990547,
"duration": 0,
"value": 0.3422101449275362,
"hadRecentInput": false,
"lastInputTime": 0
}
]
總而言之,在 3087 毫秒時,發生了 34.2% 的單一版面配置轉移。為協助找出原因,我們將使用 WebPageTest 的幻燈片檢視畫面。

捲動到影格的約 3 秒處,即可確切瞭解造成 34% 版面配置位移的原因:彩色表格。網站會非同步擷取 JSON 檔案,然後將其算繪至表格。表格一開始是空的,因此等待載入結果時填入表格會導致位移。

但這還不是全部。當網頁在約 4.3 秒時視覺上已完成,我們可以看到網頁「Is my host fast yet?」的 <h1>
突然出現。這是因為網站使用網頁字型,但未採取任何步驟來最佳化算繪作業。發生這種情況時,版面配置實際上不會位移,但使用者必須等待這麼久才能閱讀標題,仍會造成不良的使用體驗。
修正版面配置不穩定問題
我們現在知道非同步產生的表格會導致三分之一的檢視區塊位移,因此是時候修正這個問題了。在實際載入 JSON 結果之前,我們無法得知表格內容,但仍可使用某種預留位置資料填入表格,讓 DOM 呈現時的版面配置相對穩定。
以下是產生預留位置資料的程式碼:
function getRandomFiller(maxLength) {
var filler = '█';
var len = Math.ceil(Math.random() * maxLength);
return new Array(len).fill(filler).join('');
}
function getRandomDistribution() {
var fast = Math.random();
var avg = (1 - fast) * Math.random();
var slow = 1 - (fast + avg);
return [fast, avg, slow];
}
// Temporary placeholder data.
window.data = [];
for (var i = 0; i < 36; i++) {
var [fast, avg, slow] = getRandomDistribution();
window.data.push({
platform: getRandomFiller(10),
client: getRandomFiller(5),
n: getRandomFiller(1),
fast,
avg,
slow
});
}
updateResultsTable(sortResults(window.data, 'fast'));
系統會先隨機產生預留位置資料,再進行排序。包括重複隨機次數的「█」字元,用來建立文字的視覺預留位置,以及隨機產生的三個主要值分布。我也新增了一些樣式,將表格中的所有顏色去飽和,清楚指出資料尚未完全載入。
您使用的預留位置外觀不會影響版面配置穩定性。預留位置的目的是向使用者保證內容即將顯示,網頁並未損壞。
載入 JSON 資料時,預留位置會顯示如下:

解決網頁字型問題的程序簡單許多。由於網站使用 Google 字型,我們只需要在 CSS 要求中傳入 display=swap
屬性。就這樣,Fonts API 會在字型宣告中加入 font-display: swap
樣式,讓瀏覽器立即以備用字型算繪文字。以下是包含修正內容的對應標記:
<link href="https://fonts.googleapis.com/css?family=Chivo:900&display=swap" rel="stylesheet">
驗證最佳化項目
透過 WebPageTest 重新執行網頁後,我們可以產生前後比較,以視覺化方式呈現差異,並測量新的版面配置不穩定程度:

[
{
"name": "",
"entryType": "layout-shift",
"startTime": 3070.9349999997357,
"duration": 0,
"value": 0.000050272187989256116,
"hadRecentInput": false,
"lastInputTime": 0
}
]
根據自訂指標,版面配置位移仍發生在 3071 毫秒 (與先前大致相同),但位移嚴重程度大幅降低,只有 0.005%。我可以接受。
從影片格中也可以清楚看出,<h1>
字型會立即回復為系統字型,讓使用者更快讀取內容。
結論
複雜網站的版面配置位移次數可能比這個範例多,但修正程序仍相同:在 WebPageTest 中加入版面配置不穩定指標,參照結果和視覺載入膠卷,找出造成問題的原因,然後使用預留位置保留螢幕空間,藉此修正問題。
(One more thing) 測量實際使用者遇到的版面不穩定情形
能夠在最佳化前後對網頁執行 WebPageTest,並看到指標有所改善,固然是好事,但真正重要的是使用者體驗確實有所提升。這不就是我們想改善網站的原因嗎?
因此,如果我們開始測量實際使用者的版面配置不穩定體驗,以及傳統的網頁效能指標,那就太好了。這是最佳化意見回饋循環的重要環節,因為有了現場資料,我們才能瞭解問題所在,以及修正是否帶來正面影響。
除了收集自己的版面配置不穩定資料,您也可以查看 Chrome 使用者體驗報告,其中包含數百萬個網站的實際使用者體驗累積版面配置位移資料。您可以藉此瞭解自己 (或競爭對手) 的成效,也可以用來探索整個網路的版面配置不穩定狀態。