LESSON 30分

「ユーザーが感じるパフォーマンスを数値化したのがCore Web Vitalsだ」と佐藤CTOは言った。「サーバーサイドのレイテンシだけ見ていては、本当のユーザー体験は分からない。」

1. Core Web Vitals の3指標

指標意味良好改善が必要不良
LCP (Largest Contentful Paint)最大コンテンツの描画時間< 2.5s2.5-4.0s> 4.0s
INP (Interaction to Next Paint)インタラクション応答性< 200ms200-500ms> 500ms
CLS (Cumulative Layout Shift)レイアウトのずれ< 0.10.1-0.25> 0.25

LCP の改善ポイント

// LCP に影響する要素
// 1. <img> 要素(最も多い原因)
// 2. <video> のポスター画像
// 3. background-image を持つ要素
// 4. テキストブロック要素

// LCP 最適化チェックリスト
const lcpOptimizations = [
  'リソースの発見を早める(preload, fetchpriority="high")',
  'TTFB を短縮する(CDN, サーバーサイドキャッシュ)',
  'レンダリングブロックリソースを排除する(CSS/JS の遅延読み込み)',
  '画像を最適化する(WebP/AVIF, 適切なサイズ, lazy loading は LCP 画像に使わない)',
];
<!-- LCP画像の最適化例 -->
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high" />
<img src="/hero.webp" alt="Hero" width="1200" height="600"
     fetchpriority="high" decoding="async" />
<!-- LCP画像には lazy loading を使わない! -->

INP の改善ポイント

// INP = イベントハンドラの処理時間 + レンダリング時間
// 200ms以内に次のフレームを描画する必要がある

// 長いタスクを分割する
function processLargeList(items: any[]) {
  const CHUNK_SIZE = 50;
  let index = 0;

  function processChunk() {
    const end = Math.min(index + CHUNK_SIZE, items.length);
    for (let i = index; i < end; i++) {
      processItem(items[i]);
    }
    index = end;
    if (index < items.length) {
      // メインスレッドに制御を戻す
      requestIdleCallback(processChunk);
    }
  }
  processChunk();
}

CLS の改善ポイント

/* CLS を防ぐ: 画像やiframeに明示的なサイズを指定 */
img, video { aspect-ratio: attr(width) / attr(height); width: 100%; height: auto; }

/* フォントの読み込みによるレイアウトシフトを防ぐ */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap; /* または optional */
  size-adjust: 100%;
  ascent-override: 90%;
  descent-override: 20%;
}

2. 測定ツール

ツール種類用途
Lighthouseラボデータ開発中の品質チェック
WebPageTestラボデータ詳細な描画タイムライン分析
CrUX (Chrome UX Report)フィールドデータ実ユーザーの統計データ
web-vitals ライブラリフィールドデータ自前のRUM収集
// web-vitals ライブラリでフィールドデータを収集
import { onLCP, onINP, onCLS } from 'web-vitals';

function sendToAnalytics(metric: { name: string; value: number; id: string }) {
  fetch('/api/analytics/web-vitals', {
    method: 'POST',
    body: JSON.stringify({
      name: metric.name,
      value: metric.value,
      id: metric.id,
      url: window.location.href,
      timestamp: Date.now(),
    }),
    keepalive: true,
  });
}

onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);

3. フィールドデータ vs ラボデータ

観点ラボデータフィールドデータ
収集方法シミュレーション環境で測定実ユーザーのブラウザから収集
再現性高い(同条件で再測定可能)低い(ネットワーク・デバイス多様)
代表性低い(固定条件のみ)高い(実際のユーザー分布)
活用場面開発中のデバッグ・最適化本番のSLO監視・レポート
ツール例Lighthouse, WebPageTestCrUX, RUM (web-vitals)

両方を組み合わせることが重要。ラボで問題を発見・修正し、フィールドで効果を検証する。

まとめ

トピック要点
LCP2.5秒以内。画像最適化、preload、TTFBの短縮が鍵
INP200ms以内。長いタスクの分割、メインスレッドの空き確保
CLS0.1以下。明示的サイズ指定、フォント最適化
測定ラボ(Lighthouse)とフィールド(RUM)の両方を活用

チェックリスト

  • Core Web Vitalsの3指標とその閾値を説明できる
  • LCP/INP/CLSそれぞれの改善手法を知っている
  • web-vitalsライブラリでRUMデータを収集できる
  • ラボデータとフィールドデータの使い分けを理解した

次のステップへ

Core Web Vitalsの基礎を学んだ。次は バンドル最適化とコード分割 で、JavaScriptの配信を最適化しよう。

推定読了時間: 30分