Core Web Vitals とは?

Core Web Vitalsは、Googleが定義したWebページのユーザー体験を測定する3つの指標です。SEOのランキング要因にもなっています。

3つの指標

指標正式名称測定内容良好要改善
LCPLargest Contentful Paint最大コンテンツの表示速度2.5秒以下4秒以上
INPInteraction to Next Paintインタラクションの応答性200ms以下500ms以上
CLSCumulative Layout Shiftレイアウトのずれ0.1以下0.25以上

LCP(最大コンテンツの表示速度)の改善

LCPとは

ページ内で最も大きなコンテンツ(画像、テキストブロック等)が表示されるまでの時間です。

改善方法

1. 画像の最適化

<!-- NG: 巨大な画像をそのまま -->
<img src="hero.jpg" alt="Hero">

<!-- OK: 最適化された画像 -->
<img
  src="hero.webp"
  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
  sizes="(max-width: 768px) 100vw, 1200px"
  alt="Hero"
  width="1200"
  height="630"
  loading="eager"
  fetchpriority="high"
  decoding="async"
>

ポイント:

  • WebP/AVIF フォーマットを使用(JPEGより30-50%小さい)
  • srcset でレスポンシブ画像を提供
  • LCP要素には fetchpriority="high"loading="eager" を設定
  • widthheight を必ず指定(CLSも防げる)

2. クリティカルCSSのインライン化

<head>
  <!-- ファーストビューに必要なCSSをインラインで -->
  <style>
    /* クリティカルCSS(ファーストビューの表示に必要な最小限のCSS) */
    body { margin: 0; font-family: sans-serif; }
    .hero { width: 100%; height: 60vh; }
    .hero h1 { font-size: 2rem; }
  </style>

  <!-- 残りのCSSは非同期で読み込み -->
  <link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>

3. フォントの最適化

<!-- プリロードで早期取得 -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

<style>
  @font-face {
    font-family: 'MainFont';
    src: url('/fonts/main.woff2') format('woff2');
    font-display: swap;  /* フォントが読み込まれるまでシステムフォントを表示 */
  }
</style>

4. サーバーレスポンスの高速化

TTFB(Time to First Byte)を改善:
- CDNを使う
- サーバーサイドキャッシュ
- データベースクエリの最適化
- HTTP/2 or HTTP/3を有効にする

INP(インタラクションの応答性)の改善

INPとは

ユーザーの操作(クリック、タップ、キー入力)から画面が更新されるまでの時間です。

改善方法

1. 長いタスクを分割する

// NG: 長い処理がメインスレッドをブロック
function processLargeData(items) {
  items.forEach(item => {
    heavyComputation(item);  // 各アイテムに時間がかかる
  });
}

// OK: requestIdleCallback で分割
function processLargeDataInChunks(items) {
  const iterator = items[Symbol.iterator]();

  function processChunk(deadline) {
    while (deadline.timeRemaining() > 0) {
      const { value, done } = iterator.next();
      if (done) return;
      heavyComputation(value);
    }
    requestIdleCallback(processChunk);
  }

  requestIdleCallback(processChunk);
}

// OK: scheduler.yield() を使う(新しいAPI)
async function processWithYield(items) {
  for (const item of items) {
    heavyComputation(item);
    // 定期的にメインスレッドに制御を返す
    if (navigator.scheduling?.isInputPending()) {
      await scheduler.yield();
    }
  }
}

2. イベントハンドラの最適化

// NG: 重い処理をイベントハンドラ内で実行
button.addEventListener('click', () => {
  // 重い計算...
  const result = heavyComputation();
  updateUI(result);
});

// OK: UIの更新を先に、重い処理は後で
button.addEventListener('click', () => {
  // まずUIを更新(ローディング表示等)
  showLoading();

  // 重い処理は次のフレームで
  requestAnimationFrame(() => {
    setTimeout(() => {
      const result = heavyComputation();
      updateUI(result);
      hideLoading();
    }, 0);
  });
});

3. Web Worker で重い処理をオフロード

// worker.js
self.addEventListener('message', (e) => {
  const result = heavyComputation(e.data);
  self.postMessage(result);
});

// main.js
const worker = new Worker('/worker.js');
worker.postMessage(data);
worker.addEventListener('message', (e) => {
  updateUI(e.result);
});

CLS(レイアウトのずれ)の改善

CLSとは

ページの読み込み中にレイアウトが予期せずずれる度合いです。

改善方法

1. 画像・動画にサイズを指定

<!-- NG: サイズ未指定 → 読み込み完了時にレイアウトがずれる -->
<img src="photo.jpg" alt="Photo">

<!-- OK: width/height を指定 → ブラウザが領域を確保 -->
<img src="photo.jpg" alt="Photo" width="800" height="600">

<!-- OK: CSSでアスペクト比を指定 -->
<style>
  .video-wrapper {
    aspect-ratio: 16 / 9;
    width: 100%;
  }
</style>
<div class="video-wrapper">
  <iframe src="..." width="100%" height="100%"></iframe>
</div>

2. Web フォントによるずれを防ぐ

/* font-display: swap でFOUT(Flash of Unstyled Text)を許容 */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap;
}

/* size-adjust でフォールバックフォントのサイズを調整 */
@font-face {
  font-family: 'CustomFont-Fallback';
  src: local('Arial');
  size-adjust: 105%;
  ascent-override: 90%;
  descent-override: 20%;
}

3. 動的コンテンツの領域を確保

/* 広告やバナーの領域を事前に確保 */
.ad-slot {
  min-height: 250px;  /* 広告の最小高さ */
  background: #f5f5f5;
}

/* 遅延読み込みコンテンツの領域 */
.lazy-content {
  min-height: 300px;
  contain: layout;  /* レイアウトの影響範囲を制限 */
}

4. transform アニメーションを使う

/* NG: top/left でアニメーション → レイアウトシフトが発生 */
.element {
  transition: top 0.3s;
}
.element.active {
  top: 100px;
}

/* OK: transform でアニメーション → レイアウトシフトなし */
.element {
  transition: transform 0.3s;
}
.element.active {
  transform: translateY(100px);
}

JavaScript の最適化

コード分割(Code Splitting)

// 動的インポートでルートごとに分割
const HomePage = lazy(() => import('./pages/Home'));
const AboutPage = lazy(() => import('./pages/About'));

// Vite の設定
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          utils: ['lodash-es', 'date-fns'],
        },
      },
    },
  },
});

Tree Shaking

// NG: ライブラリ全体をインポート
import _ from 'lodash';
_.debounce(fn, 300);

// OK: 必要な関数だけインポート
import debounce from 'lodash-es/debounce';
debounce(fn, 300);

script タグの最適化

<!-- ブロッキングしない読み込み -->
<script src="app.js" defer></script>     <!-- HTML解析後に実行 -->
<script src="analytics.js" async></script> <!-- 非同期で読み込み・実行 -->

<!-- モジュールはデフォルトでdefer -->
<script type="module" src="app.js"></script>

測定ツール

Lighthouse

# CLI で実行
npx lighthouse https://example.com --view

# Chrome DevTools
# → Lighthouse タブ → Analyze page load

Web Vitals ライブラリ

import { onLCP, onINP, onCLS } from 'web-vitals';

onLCP(metric => {
  console.log('LCP:', metric.value);
  // アナリティクスに送信
  sendToAnalytics({ name: 'LCP', value: metric.value });
});

onINP(metric => {
  console.log('INP:', metric.value);
});

onCLS(metric => {
  console.log('CLS:', metric.value);
});

その他のツール

ツール用途
PageSpeed InsightsGoogleのパフォーマンス分析
WebPageTest詳細なウォーターフォール分析
Chrome DevTools Performanceランタイムパフォーマンス分析
Bundle Analyzerバンドルサイズの可視化

チェックリスト

カテゴリチェック項目
画像WebP/AVIF使用、srcset設定、width/height指定
フォントpreload、font-display: swap、サブセット化
JavaScriptコード分割、Tree Shaking、defer/async
CSSクリティカルCSS、未使用CSS削除
サーバーCDN、HTTP/2+、Gzip/Brotli圧縮
キャッシュCache-Control設定、Service Worker

まとめ

指標最も効果的な改善策
LCP画像最適化 + fetchpriority + CDN
INP長いタスクの分割 + Web Worker
CLSwidth/height指定 + font-display: swap

パフォーマンス改善は一度やって終わりではなく、継続的に計測・改善するサイクルが重要です。まずはLighthouseでスコアを確認し、最もインパクトの大きい項目から着手しましょう。


画像の最適化・リサイズには、Assistyの画像リサイズツールが便利です。ブラウザ上で画像を最適なサイズに変換できます。