従来、ウェブ デベロッパーにとって、ウェブページのメイン コンテンツがどれだけ速く読み込まれてユーザーに表示されるかを測定することは困難でした。load や DOMContentLoaded などの古い指標は、ユーザーが画面に表示される内容と必ずしも一致しないため、適切に機能しません。また、First Contentful Paint(FCP)などの新しいユーザー中心のパフォーマンス指標は、読み込みのごく初期の段階しか捉えません。ページにスプラッシュ画面や読み込みインジケーターが表示されている場合、そのタイミングはユーザーとあまり関係ありません。
これまでは、初回ペイント後の読み込みエクスペリエンスをより正確に把握するために、First Meaningful Paint(FMP)や Speed Index(SI)(どちらも Lighthouse で利用可能)などのパフォーマンス指標を推奨していましたが、これらの指標は複雑で説明が難しく、間違いが多々あります。つまり、ページのメイン コンテンツがいつ読み込まれたのかはまだ特定できません。
W3C ウェブ パフォーマンス ワーキング グループでの議論と Google で行われた調査に基づき、ページのメイン コンテンツが読み込まれたタイミングを測定するより正確な方法は、最も大きな要素がレンダリングされたタイミングを確認することであると判断しました。
LCP とは何ですか?
LCP は、ユーザーが最初にページにアクセスしたときと比較して、ビューポートに表示される最も大きな画像、テキスト ブロック、動画の表示時間を報告します。
良好な LCP スコアとは
優れたユーザー エクスペリエンスを提供するには、サイトで Largest Contentful Paint を 2.5 秒以下に収めるようにします。ほとんどのユーザーがこの目標値を達成できるようにするには、モバイルとパソコンのデバイスに分けてページ読み込みの75 パーセンタイルを測定することをおすすめします。
どのような要素が考慮されますか?
現在 Largest Contentful Paint API で指定されているように、Largest Contentful Paint と見なされる要素のタイプは次のとおりです。
<img>
要素(最初のフレームの表示時間は、GIF やアニメーション PNG などのアニメーション コンテンツに使用されます)<svg>
要素内の<image>
要素<video>
要素(ポスター画像の読み込み時間または動画の最初のフレームの表示時間のいずれか早い方)url()
関数を使用して読み込まれた背景画像を持つ要素(CSS グラデーションとは対照的)- テキストノードや他のインライン レベルのテキスト要素の子要素を含むブロックレベルの要素。
最初はシンプルにするために、要素をこの限定されたセットに制限しています。今後、調査が進むにつれて、<svg>
の完全なサポートなど、追加要素が追加される可能性があります。
LCP の測定では、一部の要素のみが考慮されるだけでなく、ヒューリスティクスを使用して、ユーザーが「コンテンツではない」と判断する可能性が高い特定の要素が除外されます。Chromium ベースのブラウザの場合、以下が該当します。
- 不透明度が 0 で、ユーザーには表示されない要素
- コンテンツではなく背景とみなされる可能性が高い、ビューポート全体を覆う要素
- ページの実際のコンテンツを反映していない可能性が高い、エントロピーの低いプレースホルダ画像やその他の画像
ブラウザは、最大のコンテンツ要素に関するユーザーの期待に沿うように、これらのヒューリスティクスを継続的に改善していく可能性があります。
こうした「コンテンツがある」ヒューリスティックは、First Contentful Paint(FCP)で使用されるものとは異なる場合があります。FCP では、これらの要素の一部(プレースホルダ画像や完全なビューポート画像など)が、LCP の候補とならない場合でも考慮される場合があります。どちらも名前に「contentful」を使用していますが、指標の目的は異なります。FCP は任意のコンテンツが画面に描画されるまでの時間を測定し、LCP はメイン コンテンツが描画されるまでの時間を測定するため、LCP はより選択的なものになっています。
要素のサイズはどのように決まります?
LCP で報告される要素のサイズは、通常、ビューポート内でユーザーに表示されるサイズです。要素がビューポートの外側に広がっている場合、または要素の一部が切り捨てられている場合、または表示されないオーバーフローがある場合、それらの部分は要素のサイズにカウントされません。
固有のサイズからサイズ変更された画像要素の場合、報告されるサイズは、表示サイズと固有のサイズのどちらか小さい方です。
テキスト要素の場合、LCP はすべてのテキストノードを含めることができる最小の長方形のみを考慮します。
すべての要素について、LCP では CSS を使用して適用されたマージン、パディング、境界は考慮されません。
LCP はいつ報告されますか?
ウェブページは段階的に読み込まれるため、ページ上の最大の要素が変更される可能性があります。
この変化を処理するため、ブラウザは最初のフレームをペイントするとすぐに、最大のコンテンツ要素を識別する largest-contentful-paint
タイプの PerformanceEntry
をディスパッチします。ただし、その後のフレームをレンダリングした後、Largest Contentful Element が変更されるたびに、別の PerformanceEntry
がディスパッチされます。
たとえば、テキストとヒーロー画像を含むページでは、ブラウザは最初にテキストのみをレンダリングする場合があります。この時点で、ブラウザは largest-contentful-paint
エントリをディスパッチします。このエントリの element
プロパティは <p>
または <h1>
を参照する可能性があります。その後、ヒーロー画像の読み込みが完了すると、2 つ目の largest-contentful-paint
エントリがディスパッチされ、その element
プロパティが <img>
を参照します。
要素は、レンダリングされてユーザーに表示された後で初めて、最大のコンテンツを含む要素と見なされます。まだ読み込まれていない画像は「レンダリング済み」と見なされません。どちらも、フォント ブロック期間中にウェブフォントを使用しているテキストノードではありません。このような場合、小さい要素が最大コンテンツ要素として報告されることがあります。ただし、大きい要素のレンダリングが完了するとすぐに、別の PerformanceEntry
が作成されます。
画像やフォントが遅れて読み込まれるだけでなく、新しいコンテンツが利用可能になると、ページが DOM に新しい要素を追加することもあります。これらの新しい要素のいずれかが、以前の最大のコンテンツ要素よりも大きい場合、新しい PerformanceEntry
も報告されます。
最大のコンテンツ要素がビューポートから削除された場合、または DOM から削除された場合でも、より大きな要素がレンダリングされない限り、最大のコンテンツ要素のままになります。
ユーザーが(タップ、スクロール、キー操作などで)ページを操作すると、ブラウザはすぐに新しいエントリの報告を停止します。これは、ユーザー操作によってユーザーに表示される内容が頻繁に変わるためです(特にスクロールの場合はなおさらです)。
分析では、最後にディスパッチされた PerformanceEntry
のみを分析サービスにレポートする必要があります。
読み込み時間とレンダリング時間
セキュリティ上の理由から、Timing-Allow-Origin
ヘッダーのないクロスオリジン画像に対して、画像のレンダリング タイムスタンプは公開されません。代わりに、読み込み時間のみが公開されます(他の多くのウェブ API を通じてすでに公開されているためです)。
そのため、ウェブ API によって LCP が FCP よりも早く報告されるという、一見ありえない状況につながりかねません。これは、このセキュリティ制限によるものです。
可能であれば、指標の精度を高めるために、常に Timing-Allow-Origin
ヘッダーを設定することをおすすめします。
要素のレイアウトとサイズの変更はどのように処理されますか?
新しいパフォーマンス エントリの計算とディスパッチのパフォーマンス オーバーヘッドを低く抑えるために、要素のサイズや位置を変更しても、新しい LCP 候補は生成されません。要素の初期サイズとビューポート内の位置のみが考慮されます。
つまり、最初に画面外でレンダリングされ、その後画面内に移行する画像は報告されない可能性があります。また、最初にビューポート内でレンダリングされた要素が押し下げられ、ビューポート外になった場合でも、最初のビューポート内のサイズが報告されます。
例
以下に、一般的なウェブサイトで Largest Contentful Paint が発生するタイミングの例を示します。
上記の両方のタイムラインで、コンテンツの読み込みに伴い最大要素が変化しています。最初の例では、新しいコンテンツが DOM に追加され、それによって最も大きい要素が変更されています。2 つ目の例では、レイアウトが変更され、以前は最大だったコンテンツがビューポートから削除されています。
遅れて読み込まれるコンテンツは、ページにすでに表示されているコンテンツよりも大きいことがよくありますが、必ずしもそうとは限りません。次の 2 つの例は、ページが完全に読み込まれる前に LCP が発生する状況を示しています。
最初の例では、Instagram のロゴが比較的早い段階で読み込まれ、他のコンテンツが段階的に表示されても最大の要素のままです。Google 検索の検索結果ページの例では、最も大きい要素は、画像やロゴの読み込みが完了する前に表示されるテキストの段落です。個々の画像はすべてこの段落よりも小さいため、読み込みプロセス全体を通じて最大の要素のままです。
LCP を測定する方法
LCP はラボまたは現場で測定できます。次のツールで利用できます。
フィールドツール
- Chrome ユーザー エクスペリエンス レポート
- PageSpeed Insights
- Search Console(Core Web Vitals レポート)
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
エントリを親フレームに報告し、集計できます。 - API はナビゲーション開始から LCP を測定しますが、事前レンダリングされたページの場合は、ユーザーが認識する LCP 時間に対応するため、
activationStart
から LCP を測定する必要があります。
デベロッパーは、これらの微妙な違いをすべて覚えておく代わりに、web-vitals
JavaScript ライブラリを使用して 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 のタイミングを特定し、ラボデータを使用してドリルダウンして最適化するプロセスについて説明しています。
参考情報
- Annie Sullivan が performance.now() で発表した Chrome のパフォーマンス モニタリングから得た教訓(2019 年)
変更履歴
指標の測定に使用される API や、指標自体の定義でバグが見つかることがあります。そのため、変更を加える必要がある場合があり、これらの変更は内部レポートやダッシュボードで改善または低下として表示されることがあります。
これらの指標の実装または定義に対するすべての変更は、この変更ログに表示されます。
これらの指標に関するフィードバックがある場合は、web-vitals-feedback Google グループで送信してください。