従来、ウェブ デベロッパーにとって、ウェブページのメイン コンテンツがどれほど速く読み込まれ、ユーザーに表示されてしまうのかを測定することは、課題となっていました。
load や DOMContentLoaded などの古い指標は、ユーザーに表示されるものと必ずしも一致しないため、あまり好ましくありません。また、First Contentful Paint(FCP)などの新しいユーザー中心のパフォーマンス指標では、読み込みエクスペリエンスの最初の部分しか取得されません。ページにスプラッシュ画面または読み込みインジケーターが表示される場合、そのタイミングはユーザーとはあまり関係ありません。
これまで、イニシャル ペイント後の読み込みエクスペリエンスをより詳細に把握できるように、First Meaningful Paint(FMP)や Speed Index(SI)(どちらも Lighthouse で利用可能)などのパフォーマンス指標が推奨されていましたが、これらの指標は複雑で説明が難しく、多くの場合、正しくありません。つまり、ページのメイン コンテンツがいつ読み込まれたのかを識別できません。
シンプルなほどよい場合もあります。W3C のウェブ パフォーマンス ワーキング グループでの議論と Google の調査から、ページのメイン コンテンツが読み込まれたタイミングをより正確に測定するには、最大の要素がレンダリングされたタイミングを確認することが判明しました。
LCP とは
Largest Contentful Paint(LCP)指標は、ビューポート内に表示される最も大きな画像またはテキスト ブロックのレンダリング時間を、ページが最初に読み込みを開始したタイミングを基準としてレポートします。
良い LCP スコアとは何ですか?
優れたユーザー エクスペリエンスを提供するには、Largest Contentful Paint を 2.5 秒以下にする必要があります。ほとんどのユーザーでこの目標を達成するには、ページの読み込みを 75 パーセンタイルでモバイル デバイスとデスクトップ デバイスを分けて測定するしきい値を設定することをおすすめします。
どのような要素が考慮されますか?
Largest Contentful Paint API で現在指定されているように、Largest Contentful Paint の考慮対象となる要素のタイプは次のとおりです。
<img>
要素<svg>
要素内の<image>
要素- ポスター画像を含む
<video>
要素(ポスター画像の読み込み時間が使用されます) url()
関数で読み込まれた背景画像がある要素(CSS グラデーションではない)- テキストノードやその他のインライン レベルのテキスト要素の子を含むブロックレベル要素。
<video>
要素を自動再生するために最初にペイントされたフレーム(2023 年 8 月時点)- アニメーション GIF などのアニメーション画像形式の最初のフレーム(2023 年 8 月時点)
なお、最初はシンプルにするために、要素をこのように限定したのは意図的なものです。さらなる調査が実施されるにつれて、他の要素(<svg>
の完全なサポートなど)が今後追加される可能性があります。
一部の要素のみを考慮するだけでなく、特定のヒューリスティックを適用して、ユーザーにとって「コンテンツに満足していない」とみなされる特定の要素を除外します。Chromium ベースのブラウザには、次のようなものがあります。
- 不透明度が 0 で、ユーザーに表示されない要素
- コンテンツではなく背景とみなされる可能性が高い、ビューポート全体を覆う要素
- ページの実際のコンテンツを反映していない可能性のある、エントロピーの低いプレースホルダ画像やその他の画像
ブラウザは今後もこうしたヒューリスティックを改善し、ユーザーの期待に応えるコンテンツの最大何を提供するのかについて検討してまいります。
この「コンテンツ フル」ヒューリスティックは、First Contentful Paint(FCP)で使用されるものとは異なる場合があります。FCP では、これらの要素の一部(プレースホルダ画像やフル ビューポート画像など)が、LCP 候補の対象とならない場合でも考慮される場合があります。どちらも「満足のいく」という言葉を使っていますが、これらの指標の目的は異なります。FCP は、任意のコンテンツが画面と LCP にペイントされたタイミングを測定し、その際には、LCP の選択性を高めるため、メイン コンテンツがペイントされます。
要素のサイズはどのように決まるのですか?
Largest Contentful Paint についてレポートされる要素のサイズは、通常、ビューポート内でユーザーに表示されるサイズです。要素がビューポートの外部に拡張される場合、またはいずれかの要素がクリップされている場合や目に見えないoverflowがある場合、その部分は要素のサイズにカウントされません。
固有のサイズからサイズ変更された画像要素の場合、報告されるサイズは表示サイズまたは組み込みサイズのいずれか小さい方になります。たとえば、固有のサイズよりもはるかに小さいサイズに縮小された画像は、表示されるサイズのみが報告されますが、大きいサイズに引き伸ばされたり拡大されたりした画像は、固有のサイズのみが報告されます。
テキスト要素の場合は、そのテキストノード(すべてのテキストノードを囲む最小の長方形)のサイズのみが考慮されます。
すべての要素について、CSS を通じて適用されるマージン、パディング、枠線は考慮されません。
Large Contentful Paint が報告されるのはいつですか?
ウェブページは段階的に読み込まれることが多いため、ページの最大要素が変更される可能性があります。
このような変更の可能性に対処するために、ブラウザは最初のフレームを描画した後すぐに、最も大きなコンテンツ要素を示す largest-contentful-paint
型の PerformanceEntry
をディスパッチします。ただし、後続のフレームをレンダリングした後、最もコンテンツの多い要素が変化するたびに、別の PerformanceEntry
をディスパッチします。
たとえば、テキストとヒーロー画像を含むページでは、ブラウザは最初にテキストをレンダリングするだけです。その時点でブラウザは、element
プロパティが <p>
または <h1>
を参照する可能性が高い largest-contentful-paint
エントリをディスパッチします。その後、ヒーロー画像の読み込みが完了すると、2 つ目の largest-contentful-paint
エントリがディスパッチされ、その element
プロパティが <img>
を参照します。
要素は、レンダリングされてユーザーに表示されるまでは、最大のコンテンツ要素と見なすことができることに注意してください。まだ読み込まれていない画像は「レンダリングされた」とは見なされません。フォント ブロック期間は、ウェブフォントを使用するテキストノードも例外です。このような場合、小さい要素が最大コンテンツ要素として報告されますが、大きい要素がレンダリングを完了するとすぐに別の PerformanceEntry
オブジェクトを介して報告されます。
遅延読み込み画像やフォントだけでなく、新しいコンテンツが利用可能になったときに、ページでは DOM に新しい要素が追加される場合があります。これらの新しい要素のいずれかが、以前の最大コンテンツ要素よりも大きい場合、新しい PerformanceEntry
も報告されます。
現在、最もコンテンツの多い要素がビューポートから削除されても(または DOM からも削除された)、それよりも大きい要素がレンダリングされない限り、その要素は最もコンテンツの多い要素のままです。
ユーザーがページを操作(タップ、スクロール、キー操作)すると、ブラウザはすぐに新しいエントリの報告を停止します。これは、ユーザー操作によってユーザーに表示される内容が変化することが多いためです(これは特にスクロールで顕著です)。
分析を行う際は、最後にディスパッチされた PerformanceEntry
のみを分析サービスに報告する必要があります。
読み込み時間とレンダリング時間の比較
セキュリティ上の理由から、Timing-Allow-Origin
ヘッダーのないクロスオリジン イメージでは、画像のレンダリング タイムスタンプは公開されません。代わりに、読み込み時間のみが公開されています(他の多くのウェブ API ですでに公開されているため)。
このため、LCP が FCP より早い時期にウェブ API から報告されるという一見不可能に思える状況が発生する可能性があります。これはあくまでも、セキュリティ制限により、そうとしか考えられないためです。
可能であれば、指標の精度を高めるために Timing-Allow-Origin
ヘッダーを設定することをおすすめします。
要素のレイアウトとサイズの変更はどのように処理されますか?
新しいパフォーマンス エントリの計算とディスパッチのパフォーマンス オーバーヘッドを低く抑えるため、要素のサイズや位置を変更しても新しい LCP 候補が生成されません。ビューポート内の要素の初期サイズと位置のみが考慮されます。
つまり、最初に画面外にレンダリングされた後で画面上に遷移した画像は、報告されない場合があります。また、最初にビューポートにレンダリングされた要素が、画面外に押し下げられた場合であっても、ビューポート内の最初のサイズとしてレポートされます。
例
人気のあるウェブサイトで Largest Contentful Paint が発生する例を次に示します。
上記のどちらのタイムラインでも、コンテンツが読み込まれると、最も大きい要素が変化します。最初の例では、新しいコンテンツが DOM に追加され、どの要素が最も大きいかが変更されます。2 番目の例では、レイアウトが変更され、以前は最大だったコンテンツがビューポートから削除されます。
遅延読み込みのコンテンツは、ページ上の既存のコンテンツよりも大きくなることがよくありますが、必ずしもそうであるとは限りません。次の 2 つの例は、ページが完全に読み込まれる前に Largest Contentful Paint が発生する例です。
最初の例では、Instagram のロゴは比較的早い段階で読み込まれ、他のコンテンツが段階的に表示される場合でも最大の要素のままです。Google 検索結果ページの例では、最大の要素は、画像やロゴの読み込みが完了する前に表示されるテキストの段落です。個々の画像はすべてこの段落よりも小さいため、読み込みプロセス全体で最大の要素のままです。
LCP の測定方法
LCP はラボまたは現場で測定でき、次のツールで利用できます。
フィールド ツール
- Chrome ユーザー エクスペリエンス レポート
- PageSpeed Insights
- Search Console(ウェブに関する主な指標レポート)
web-vitals
JavaScript ライブラリ
ラボ用ツール
JavaScript で LCP を測定する
JavaScript で LCP を測定するには、Largest Contentful Paint API を使用します。次の例は、largest-contentful-paint
エントリをリッスンし、コンソールにログを記録する PerformanceObserver
の作成方法を示しています。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
上記の例では、ログに記録された各 largest-contentful-paint
エントリが現在の LCP 候補を表しています。一般に、最後に出力されるエントリの startTime
値は LCP 値ですが、常にそうなるとは限りません。すべての largest-contentful-paint
エントリが LCP の測定に有効とは限りません。
次のセクションでは、API のレポート内容と指標の計算方法の違いについて説明します。
指標と API の違い
- API はバックグラウンドのタブで読み込まれたページに対して
largest-contentful-paint
エントリを送信しますが、それらのページは LCP の計算時に無視する必要があります。 - ページがバックグラウンドに移動された後も API は
largest-contentful-paint
エントリを引き続きディスパッチしますが、LCP を計算するときはこれらのエントリを無視する必要があります(要素がずっとフォアグラウンド表示されていた場合にのみ要素が考慮されます)。 - ページがバックフォワード キャッシュから復元される場合、API は
largest-contentful-paint
エントリを報告しませんが、このような場合、ユーザーが個別のページ訪問として経験するため、LCP を測定する必要があります。 - API では iframe 内の要素は考慮されませんが、指標はページのユーザー エクスペリエンスの一部であるものとして考慮されます。iframe 内に LCP を含むページ(埋め込み動画にポスター画像が表示されるなど)では、CrUX と RUM の違いとして表示されます。LCP を適切に測定するには、この点を考慮する必要があります。サブフレームは API を使用して、集計のためにその
largest-contentful-paint
エントリを親フレームに報告できます。
デベロッパーは、こうした微妙な違いをすべて記憶するのではなく、web-vitals
JavaScript ライブラリを使用して LCP を測定できます。LCP はこれらの違いを自動的に処理します(可能な場合、iframe の問題は対象外です)。
import {onLCP} from 'web-vitals';
// Measure and log LCP as soon as it's available.
onLCP(console.log);
JavaScript で LCP を測定する方法の完全な例については、onLCP()
のソースコードをご覧ください。
最大の要素が最も重要な要素でない場合
ページ上で最も重要な要素(1 つまたは複数)が最大要素と同じでない場合、代わりにそうした他の要素のレンダリング時間の測定のほうがデベロッパーにとっては関心が高い場合があります。これは、カスタム指標に関する記事で説明されているように、Element Timing API を使用して行うことができます。
LCP を改善する方法
現場での LCP のタイミングを特定し、ラボデータを使用してドリルダウンして最適化するプロセスについては、LCP の最適化に関する詳細なガイドを参照してください。
補足資料
変更ログ
指標の測定に使用する API や、指標自体の定義でバグが発見されることがあります。そのため、必要な変更を行う必要が生じることもあるため、こうした変更は社内のレポートやダッシュボードで改善または回帰として現れる場合があります。
これを簡単に管理できるように、これらの指標の実装または定義に対するすべての変更は、この CHANGELOG に表示されます。
これらの指標についてフィードバックがございましたら、web-vitals-feedback Google グループでお知らせください。