使用 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 穩定版推出後,這個程序變得更簡單。系統會預設啟用 Layout Instability 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});
});
這個指令碼中的承諾會解析為陣列的 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
屬性。就這樣。字型 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 中新增版面配置不穩定度指標、交叉比對結果與視覺載入膠卷,以找出問題所在,並使用預留位置來保留螢幕空間,實作修正項目。
(再補充一點) 評估實際使用者遇到的版面配置不穩定情形
在最佳化前後,您可以對網頁執行 WebPageTest,並查看指標的改善情形,這當然很不錯,但真正重要的是,使用者體驗確實有所改善。這不就是我們一開始就想改善網站的原因嗎?
因此,如果我們能開始評估實際使用者的版面配置不穩定體驗,並納入傳統的網站效能指標,那就太好了。這是最佳化意見回饋循環的重要環節,因為實地資料可讓我們瞭解問題所在,以及修正方式是否有效。
除了收集自己的版面配置不穩定資料,也請查看 Chrome 使用者體驗報告,其中包含來自數百萬個網站的實際使用者體驗累積版面配置偏移資料。您可以使用這項工具瞭解自己或競爭對手的成效,也可以探索網站上版面配置不穩定的狀態。