レイアウトの不安定性の修正

WebPageTest を使用してレイアウトの不安定性の問題を特定して修正する方法について説明します。

以前の投稿で、WebPageTest の Cumulative Layout Shift(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% の非常に小さなシフトが 1 つ発生しています。

変化の時期と重大度を知ることは、変化の原因を突き止めるのに役立ちます。ラボ環境の 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});
});

このスクリプトの Promise は、配列自体ではなく配列の JSON 表現に解決されます。これは、カスタム指標で生成できるのは文字列や数値などのプリミティブ データ型のみであるためです。

テストに使用するウェブサイトは ismyhostfastyet.com です。これは、ウェブホストの実際の読み込みパフォーマンスを比較するために構築したサイトです。

レイアウトが不安定になる原因の特定

結果では、LayoutShifts カスタム指標に次の値があることがわかります。

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3087.2349999990547,
    "duration": 0,
    "value": 0.3422101449275362,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

まとめると、3, 087 ミリ秒で 34.2% の単一のレイアウト シフトが発生します。原因を特定するため、WebPageTest の Filmstrip ビューを使用します。

フィルムストリップ内の 2 つのセル。レイアウト シフトの前後のスクリーンショットが表示されている。
レイアウト シフトの前後のスクリーンショットを示すフィルムストリップ内の 2 つのセル。

フィルムストリップを 3 秒以内のマークまでスクロールすると、34% のレイアウト シフトの原因がカラフルなテーブルであることがわかります。ウェブサイトは JSON ファイルを非同期で取得し、テーブルにレンダリングします。テーブルは最初は空であるため、結果の読み込み時にテーブルがいっぱいになるまで待機すると、シフトが発生します。

ウェブフォント ヘッダーが突然表示される。
ウェブフォント ヘッダーが突然表示される。

それだけではありません。ページが視覚的に完了するまでの時間が 4.3 秒以内になると、「ホストは速いですか?」ページの <h1> がどこからともなく表示されます。これは、サイトでウェブフォントを使用していて、レンダリングを最適化する対策を一切行っていないことが原因です。この場合、実際にレイアウトが変化しているようには見えませんが、タイトルを読むのに長時間待たなければならないというユーザー エクスペリエンスは依然として低下します。

レイアウトの不安定性の修正

非同期に生成されたテーブルによってビューポートの 3 分の 1 がずれていることがわかったので、次はこの問題を修正します。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'));

プレースホルダ データは、並べ替える前にランダムに生成されます。「█」文字がランダムな回数繰り返されてテキストの視覚的なプレースホルダが作成され、3 つの主要値のランダムに生成された分布が作成されます。また、データがまだ完全に読み込まれていないことを明確にするため、テーブルの全色の彩度を下げるスタイルも追加しました。

使用するプレースホルダの外観は、レイアウトの安定性には関係ありません。プレースホルダの目的は、コンテンツがすでに表示され、ページが壊れていないことをユーザーに保証することです。

JSON データの読み込み中のプレースホルダは次のようになります。

データテーブルがプレースホルダ データでレンダリングされます。
データテーブルがプレースホルダ データでレンダリングされます。

ウェブフォントの問題はずっと簡単に解決できます。このサイトでは Google Fonts を使用しているため、必要なのは CSS リクエストで display=swap プロパティを渡すことだけです。これで終わりです。Fonts API でフォント宣言に font-display: swap スタイルが追加され、ブラウザが代替フォントでテキストをすぐにレンダリングできるようになります。修正を含む対応するマークアップは次のとおりです。

<link href="https://fonts.googleapis.com/css?family=Chivo:900&display=swap" rel="stylesheet">

最適化の検証

WebPageTest でページを再実行した後、変更前後の比較を生成して違いを可視化し、レイアウトの不安定性の新たな程度を測定できます。

レイアウトの最適化を有効にした場合と無効にした場合の両方のサイトが読み込まれる WebPageTest のフィルムストリップ。
レイアウトの最適化を有効にした場合と無効にした場合の両方のサイトが横並びで読み込まれる WebPageTest のフィルムストリップ。
[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3070.9349999997357,
    "duration": 0,
    "value": 0.000050272187989256116,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

カスタム指標によれば、3, 071 ミリ秒(以前とほぼ同じ)でまだレイアウト シフトが発生していますが、シフトの重大度は 0.005% とはるかに小さくなっています。私はこれでいいです。

また、<h1> フォントがすぐにシステム フォントにフォールバックし、ユーザーが早く読めるようになることも、フィルムストリップから明らかです。

まとめ

複雑なウェブサイトでは、この例よりも多くのレイアウト シフトが発生する可能性がありますが、修正プロセスは同じです。WebPageTest にレイアウトの不安定性の指標を追加し、結果を視覚的な読み込みフィルムストリップと相互参照して問題の原因を特定し、プレースホルダを使用して画面領域を予約して修正します。

(もう 1 つ)実際のユーザーが経験するレイアウトの不安定性を測定する

最適化の前後にページで WebPageTest を実行して指標の改善を確認できるのは便利ですが、本当に重要なのはユーザー エクスペリエンスが実際に向上していることです。そもそもサイトの改善に取り組んでいるのではありませんか?

ですから、従来のウェブ パフォーマンス指標とともに、実際のユーザーによるレイアウトの不安定性に関するエクスペリエンスの測定を始めれば、素晴らしいものになるでしょう。これは、最適化のフィードバック ループの重要な要素です。フィールドからのデータを得ることで、問題がどこにあるのか、修正がプラスの効果をもたらしたかどうかがわかるからです。

レイアウトの不安定性に関するお客様独自のデータを収集することに加え、Chrome UX レポートもご覧ください。このレポートには、何百万ものウェブサイトでの実際のユーザー エクスペリエンスから得られた Cumulative Layout Shift のデータが記載されています。お客様(または競合他社)のパフォーマンスを確認したり、ウェブ全体でレイアウトが不安定な状態になっているかどうかを調べたりできます。