LESSON 30分

ストーリー

高橋アーキテクト
バックエンドのパフォーマンスは改善できた。でも、ユーザーが”遅い”と感じるのはフロントエンドの問題であることも多い

高橋アーキテクトが続けた。

高橋アーキテクト
GoogleはCore Web Vitalsという指標を定義した。LCP、FID、CLS。この3つを理解して改善できれば、ユーザー体験は劇的に向上する。しかもSEOにも直結する

Core Web Vitals とは

Googleが定義した、ユーザー体験の品質を測定する3つの指標です。

1. LCP(Largest Contentful Paint)

ページの最も大きなコンテンツ要素が表示されるまでの時間です。

// LCPの対象となる要素
const lcpTargetElements = [
  '<img> 要素',
  '<video> のポスター画像',
  'CSSの background-image',
  'テキストブロック(<p>, <h1> など)',
];

// LCPの目標値
const lcpThresholds = {
  good: 2500,      // 2.5秒以下: 良好
  needsImprovement: 4000, // 4秒以下: 改善が必要
  poor: Infinity,   // 4秒超: 不良
};

改善方法:

// 1. 画像の遅延読み込みとpreload
// Hero画像は preload で先読み
// <link rel="preload" as="image" href="/hero.webp">

// 2. サーバーサイドレンダリング(SSR)でTTFBを短縮
// 3. CDNで静的アセットを配信
// 4. レンダリングブロックリソースを最小化

2. INP(Interaction to Next Paint)

ユーザーの操作(クリック、タップ、キー入力)からUIが更新されるまでの時間です。FIDの後継指標です。

// INPの目標値
const inpThresholds = {
  good: 200,        // 200ms以下: 良好
  needsImprovement: 500,  // 500ms以下: 改善が必要
  poor: Infinity,    // 500ms超: 不良
};

// INPが悪化する原因
const inpBottlenecks = [
  'メインスレッドを長時間占有するJavaScript',
  '大量のDOM操作',
  '同期的なレイアウト計算(Forced Reflow)',
  '重いイベントハンドラー',
];

// 改善例
class UIOptimizer {
  // 重い処理をチャンクに分割
  async processLargeList(items: Item[]): Promise<void> {
    const CHUNK_SIZE = 50;
    for (let i = 0; i < items.length; i += CHUNK_SIZE) {
      const chunk = items.slice(i, i + CHUNK_SIZE);
      this.renderChunk(chunk);

      // ブラウザに制御を返す(UIの応答性を維持)
      await new Promise(resolve => requestAnimationFrame(resolve));
    }
  }

  // Web Workerで重い計算をオフロード
  calculateInWorker(data: Data): Promise<Result> {
    return new Promise((resolve) => {
      const worker = new Worker('/workers/calculator.js');
      worker.postMessage(data);
      worker.onmessage = (e) => resolve(e.data);
    });
  }
}

3. CLS(Cumulative Layout Shift)

ページ読み込み中のレイアウトのずれの累積値です。

// CLSの目標値
const clsThresholds = {
  good: 0.1,         // 0.1以下: 良好
  needsImprovement: 0.25,  // 0.25以下: 改善が必要
  poor: Infinity,     // 0.25超: 不良
};

// CLSが発生する原因と対策
const clsFixStrategies = {
  // 原因1: 画像のサイズ未指定
  images: {
    problem: '画像読み込み後にレイアウトがずれる',
    fix: '<img width="800" height="600"> のように明示的にサイズ指定',
    css: 'aspect-ratio: 16/9; で比率を維持',
  },

  // 原因2: 動的に挿入されるコンテンツ
  dynamicContent: {
    problem: '広告やバナーが後から挿入される',
    fix: '事前にスペースを確保しておく(min-height指定)',
  },

  // 原因3: Webフォントの読み込み
  fonts: {
    problem: 'フォント切り替え時にテキストサイズが変わる',
    fix: 'font-display: swap; + size-adjust で差異を最小化',
  },
};

計測方法

Lighthouse

// Lighthouse CI の設定例
const lighthouseConfig = {
  ci: {
    collect: {
      url: ['https://speedshop.example.com/'],
      numberOfRuns: 3,
    },
    assert: {
      assertions: {
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'interactive': ['error', { maxNumericValue: 3500 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
      },
    },
  },
};

Web Vitals API

// フロントエンドで実際のユーザーデータを計測
import { onLCP, onINP, onCLS } from 'web-vitals';

function sendToAnalytics(metric: Metric): void {
  fetch('/api/analytics/web-vitals', {
    method: 'POST',
    body: JSON.stringify({
      name: metric.name,
      value: metric.value,
      rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'
      page: window.location.pathname,
    }),
  });
}

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

指標の優先順位

優先度指標理由
最優先LCP最初の視覚的な体験を決定
INP操作への応答性はUXの核心
CLSレイアウトのずれは誤操作を招く

まとめ

ポイント内容
LCP最大コンテンツの表示時間。2.5秒以下が目標
INP操作への応答時間。200ms以下が目標
CLSレイアウトのずれ。0.1以下が目標
計測Lighthouse + Web Vitals API でラボ/フィールド両方計測
SEOCore Web VitalsはGoogleの検索ランキング要因

チェックリスト

  • LCP、INP、CLSの意味と目標値を説明できる
  • 各指標の主な悪化原因を知った
  • Lighthouseでの計測方法を理解した
  • Web Vitals APIでの実ユーザー計測を把握した

次のステップへ

次は「バンドル最適化」を学びます。JavaScriptのバンドルサイズを削減し、LCPとINPを改善する技法です。


推定読了時間: 30分