パフォーマンス

Core Web Vitals(LCP・CLS・INP)をまず確認する

読了 7分
パフォーマンス

Core Web Vitals(LCP・CLS・INP)をまず確認する

サイトの表示が遅いと指摘されて、Core Web Vitalsをちゃんと見るようになったので、そのときに調べたことのメモです。

Core Web Vitalsは、Googleが定めた「ユーザー体験の質」を測る指標群です。読み込みの速さ、表示の安定性、操作への反応の3つを数値化したもので、検索評価にも影響するようです。まずは各指標の意味と目標値を押さえて、そのうえで実際に試しやすい打ち手を整理します。

3つの指標と目標値

現在の主要指標は次の3つです。目安となる「良好(good)」のしきい値もあわせて載せておきます。

指標測るもの良好の目安
LCP(Largest Contentful Paint)最大要素が表示されるまでの時間2.5秒以下
CLS(Cumulative Layout Shift)表示中のレイアウトのズレ量0.1以下
INP(Interaction to Next Paint)操作への反応の遅さ200ミリ秒以下

これらは実際のユーザー環境(フィールドデータ)で評価されます。手元のChrome DevToolsのLighthouseはあくまで参考値で、最終的にはGoogle Search ConsoleやPageSpeed Insightsで実測を確認するのが確実なようです。手元では速かったのに本番では遅い、ということがあるので、ここは注意してください。

まずLCPを改善する

LCPは多くの場合、ファーストビューの大きな画像や見出しの表示時間で決まります。やることはこのあたりです。

  • 最大要素の画像を優先読み込みする。Next.jsなら、ヒーロー画像に priority を付けて遅延読み込みを外します(priority を指定した画像は自動でプリロードされます)。
import Image from 'next/image'

export default function Hero() {
  return <Image src="/hero.jpg" alt="" width={1200} height={600} priority />
}
  • 画像を軽くする。WebPやAVIFといった現代的なフォーマットを使い、表示サイズに見合った解像度で配信します。
  • 重要なリソースを早く取得する。フォントやヒーロー画像に preload を指定し、レンダリングを妨げるスクリプトを減らします。
  • サーバーの応答を速くする。キャッシュやCDNでTTFB(最初のバイトまでの時間)を縮めると、LCPも連動して縮みます。

このあたりを直すと、体感がけっこう変わりました。

次にCLSを改善する

CLSは「読み込み中に要素がガタッとずれる」量です。ボタンを押そうとした瞬間に広告が割り込んで誤タップ、という嫌な体験はこの指標で捉えられます。自分も一度やられたことがあるので、これは地味に重要です。

  • 画像と動画に寸法を指定するwidth / heightaspect-ratio を与えて、読み込み前から場所を確保します。
img {
  aspect-ratio: 16 / 9;
  width: 100%;
  height: auto;
}
  • 広告や埋め込みの枠をあらかじめ確保する。後から入るコンテンツのために、最小の高さを取っておきます。
  • Webフォントの差し替えでずらさないfont-display: swap と、字形の近い代替フォントの指定でズレを抑えます。
  • 新しい要素は既存コンテンツの上に挿入しない。差し込むなら、レイアウトを押し下げない位置にします。

INPを改善する

INPは、クリックやキー入力などの操作に対して、次の描画が出るまでの遅さを測ります。重いJavaScriptがメインスレッドを占有していると悪化します。

  • メインスレッドの長い処理を分割する。重い計算は小さく区切り、必要なら requestIdleCallback や分割実行で他の処理に道を譲ります。
  • 不要な再レンダリングを減らす。Reactなら、状態の持ち方を見直して、入力のたびにツリー全体が再描画されないようにします。
  • 送るJavaScriptを減らす。使っていないライブラリを外し、コード分割で初期バンドルを小さくします。
  • 入力の反映を即時にする。重い処理は操作直後の描画と切り離し、まず見た目を更新してから裏で処理します。

計測を習慣にする

改善は「測ってから」が鉄則だと思います。推測で手を入れる前に、まず現状を数字で押さえます。自分は最初これをサボって、効かない場所をいじって時間を溶かしました。まぁしかたないですね。

  • PageSpeed Insightsでフィールドデータ(実ユーザーの値)を確認する
  • Chrome DevToolsのPerformanceパネルで、どの処理が長いかを見る
  • web-vitals ライブラリで本番の値を継続的に集める
import { onLCP, onCLS, onINP } from 'web-vitals'

onLCP(console.log)
onCLS(console.log)
onINP(console.log)

これで本番の値が見えるようになります。

まとめ

  • LCP … 最大要素を速く出す(優先読み込み・画像最適化・TTFB短縮)
  • CLS … 場所を先に確保してズレを防ぐ(寸法指定・枠の確保)
  • INP … メインスレッドを空ける(処理分割・JS削減・再描画削減)

3指標はそれぞれ原因が違うので、まず計測でボトルネックを特定してから手を入れるのが近道だと思います。指標の定義や最新のしきい値はweb.devのCore Web Vitals解説が一次情報として参考になります。以上です。