SJ blog
frontend
S

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

Web Vitals完全ガイド:Core Web Vitalsを改善する具体的手法

LCP・INP・CLSの計測方法と改善策を実例で解説。Lighthouseスコアを上げるだけでなく、実際のユーザー体験を改善するための実践的アプローチを紹介します。

一言結論

LCPはfetchpriority="high"と画像フォーマット最適化で、CLSはサイズ属性の明示とフォントのfont-display設定で、INPはメインスレッドのタスク分割で、それぞれ大幅に改善できる。

Core Web Vitals とは

Google が定義する、ユーザー体験を測る3つの指標です。SEO ランキングにも影響します。

指標意味良好要改善
LCPLargest Contentful Paint(最大コンテンツ描画)≤ 2.5s> 4.0s
INPInteraction to Next Paint(インタラクション応答)≤ 200ms> 500ms
CLSCumulative Layout Shift(累積レイアウトシフト)≤ 0.1> 0.25

INP は 2024年3月に FID(First Input Delay)を置き換えました。

LCP の改善

LCP は「最も大きな要素(画像や見出しテキスト)が描画されるまでの時間」です。

画像の最適化

<!-- ❌ 最適化前 -->
<img src="hero.jpg" />

<!-- ✅ 最適化後 -->
<img
  src="hero.webp"
  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
  sizes="(max-width: 768px) 100vw, 800px"
  fetchpriority="high"
  loading="eager"
  width="1200"
  height="600"
  alt="ヒーロー画像"
/>
  • fetchpriority="high" — LCP 対象画像はブラウザに優先度を伝える
  • width / height — CLS 対策にもなる
  • WebP / AVIF — JPEG より 25〜50% 小さい

フォントの最適化

<!-- プリロードで FOUT(スタイルなしテキストの点滅)を防ぐ -->
<link rel="preload" href="/fonts/NotoSansJP.woff2" as="font" type="font/woff2" crossorigin />

<style>
  @font-face {
    font-family: "NotoSansJP";
    src: url("/fonts/NotoSansJP.woff2") format("woff2");
    font-display: swap; /* フォント読み込み中もテキスト表示 */
  }
</style>

INP の改善

INP は「クリック・キー入力・タップへの応答速度」です。JavaScriptのメインスレッドをブロックしないことが最重要。

長いタスクを分割する

// ❌ メインスレッドを500ms以上ブロック
function processLargeList(items) {
  items.forEach(heavyProcess);
}

// ✅ scheduler.yield() でチャンク処理
async function processLargeList(items) {
  for (let i = 0; i < items.length; i++) {
    heavyProcess(items[i]);
    if (i % 50 === 0) {
      await scheduler.yield(); // ブラウザにコントロールを返す
    }
  }
}

React での不要な再レンダリングを防ぐ

// ❌ 親の状態が変わるたびに子が再レンダリング
function Parent() {
  const [count, setCount] = useState(0);
  return <HeavyChild data={staticData} />;
}

// ✅ memo で再レンダリングを抑制
const HeavyChild = memo(({ data }) => <div>...</div>);

CLS の改善

CLS は「ページ読み込み中に要素が突然動く」問題です。

画像・広告に寸法を指定する

/* ❌ 寸法なし → 読み込み後にシフト発生 */
img { width: 100%; }

/* ✅ aspect-ratio で事前にスペース確保 */
img {
  width: 100%;
  aspect-ratio: 16 / 9;
}

動的コンテンツのスペース確保

/* 広告やバナーのプレースホルダー */
.ad-container {
  min-height: 250px;
  contain: layout;
}

計測ツール

Lighthouse(Chrome DevTools): ラボ環境での計測
PageSpeed Insights: フィールドデータ(実ユーザー)も確認可能
web-vitals npm パッケージ: コードで計測
Chrome UX Report (CrUX): 28日間の実ユーザーデータ
// web-vitals パッケージでコード計測
import { onLCP, onINP, onCLS } from "web-vitals";

onLCP(metric => sendToAnalytics(metric));
onINP(metric => sendToAnalytics(metric));
onCLS(metric => sendToAnalytics(metric));

まとめ

指標最重要対策
LCPヒーロー画像の WebP 化 + fetchpriority="high"
INP長いタスクを scheduler.yield() で分割
CLS画像・動的要素に width/height または aspect-ratio

Lighthouse スコアだけでなく、PageSpeed Insights の「フィールドデータ」で実ユーザーの体験を確認することが大切です。


参考: web.dev - Core Web Vitals / web-vitals npm