使用 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 使用者體驗報告,其中包含來自數百萬個網站的實際使用者體驗累積版面配置偏移資料。您可以透過這項工具瞭解自己 (或競爭對手) 的成效,或是瞭解網路上版面配置的不穩定狀態。