Webサイトの表示速度とユーザー体験は、SEOランキングやコンバージョン率に直結する重要な要素です。Googleが提唱するCore Web Vitalsは、Webページのパフォーマンスを「読み込み速度」「インタラクティビティ」「視覚的安定性」の3つの観点から評価するための指標群です。

この記事では、Core Web Vitalsを構成する3つの指標(LCP、INP、CLS)の詳細から、LighthouseやPageSpeed Insightsを使った計測方法、そして具体的なパフォーマンス改善手法まで、Webパフォーマンス最適化の全体像を解説します。

この記事で学べること

  • Core Web Vitalsの3つの指標(LCP、INP、CLS)の意味と評価基準
  • LighthouseとPageSpeed Insightsを使ったパフォーマンス計測方法
  • 画像最適化、遅延読み込み、Critical CSSなどの具体的な改善手法
  • Core Web Vitalsスコアを向上させるベストプラクティス

前提知識と実行環境

この記事を理解するために必要な前提知識と実行環境は以下の通りです。

項目 内容
前提知識 HTML/CSS/JavaScriptの基礎、ブラウザの仕組みの基本
確認環境 Google Chrome 131以降、Chrome DevTools、Lighthouse 12
対象読者 SEOやUX向上を目指すフロントエンド開発者、表示速度改善に取り組むエンジニア

Core Web Vitalsとは

Core Web Vitalsは、Googleがユーザー体験の品質を測定するために定義した主要なパフォーマンス指標です。2024年3月以降、以下の3つの指標で構成されています。

graph LR
    subgraph Core Web Vitals
        LCP[LCP<br/>読み込み速度]
        INP[INP<br/>インタラクティビティ]
        CLS[CLS<br/>視覚的安定性]
    end
    
    LCP --> UX[ユーザー体験]
    INP --> UX
    CLS --> UX
    UX --> SEO[SEOランキング]

3つの指標の概要

指標 正式名称 測定対象 良好な値
LCP Largest Contentful Paint ページ内の最大コンテンツが表示されるまでの時間 2.5秒以下
INP Interaction to Next Paint ユーザー操作から視覚的フィードバックまでの時間 200ミリ秒以下
CLS Cumulative Layout Shift ページ読み込み中のレイアウトのずれ 0.1以下

これらの指標は、Googleの検索ランキング要因の一つとして使用されており、Core Web Vitalsのスコア改善はSEO対策としても重要です。

LCP(Largest Contentful Paint)の詳細と改善方法

LCPは、ビューポート内で最も大きなコンテンツ要素が表示されるまでの時間を測定する指標です。ユーザーが「ページが読み込まれた」と感じるタイミングを表現しています。

LCPの評価基準

スコア 評価 ユーザー体験
2.5秒以下 良好(緑) ページの読み込みが速いと感じる
2.5秒〜4.0秒 改善が必要(オレンジ) やや待たされる印象を受ける
4.0秒超 不良(赤) ページが重いと感じ、離脱の可能性が高まる

LCPの対象となる要素

LCPの計測対象となる要素は以下の通りです。

  • <img>要素
  • <svg>内の<image>要素
  • <video>要素のポスター画像
  • background-imageを持つ要素(CSS)
  • テキストノードを含むブロックレベル要素

LCP改善の具体的な手法

1. サーバーレスポンス時間の短縮

サーバーからの応答時間(TTFB: Time to First Byte)を短縮することで、LCPを改善できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Nginx でのgzip圧縮設定
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
gzip_min_length 1000;
gzip_comp_level 6;

# キャッシュヘッダーの設定
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

2. 画像の最適化

LCP要素が画像の場合、次世代フォーマットと適切なサイズ指定が重要です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!-- レスポンシブ画像の実装 -->
<picture>
  <source 
    srcset="hero-480.webp 480w, hero-800.webp 800w, hero-1200.webp 1200w"
    sizes="(max-width: 600px) 480px, (max-width: 1000px) 800px, 1200px"
    type="image/webp">
  <source 
    srcset="hero-480.jpg 480w, hero-800.jpg 800w, hero-1200.jpg 1200w"
    sizes="(max-width: 600px) 480px, (max-width: 1000px) 800px, 1200px"
    type="image/jpeg">
  <img 
    src="hero-800.jpg" 
    alt="ヒーロー画像" 
    width="1200" 
    height="600"
    fetchpriority="high">
</picture>

3. LCP要素のプリロード

LCP対象となる画像やフォントを事前に読み込むことで、表示を高速化できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<head>
  <!-- LCP画像のプリロード -->
  <link rel="preload" as="image" href="/images/hero.webp" type="image/webp">
  
  <!-- Webフォントのプリロード -->
  <link rel="preload" as="font" href="/fonts/main.woff2" type="font/woff2" crossorigin>
  
  <!-- 重要なCSSのプリロード -->
  <link rel="preload" as="style" href="/css/critical.css">
</head>

4. レンダリングブロックリソースの削除

JavaScriptやCSSがレンダリングをブロックしないように最適化します。

1
2
3
4
5
6
7
8
<!-- 非同期でJavaScriptを読み込む -->
<script src="analytics.js" async></script>

<!-- DOMの読み込み後に実行 -->
<script src="non-critical.js" defer></script>

<!-- 条件付きでCSSを読み込む -->
<link rel="stylesheet" href="print.css" media="print">

INP(Interaction to Next Paint)の詳細と改善方法

INPは、2024年3月にFID(First Input Delay)に代わってCore Web Vitalsの指標となりました。ページ全体を通じたユーザーインタラクションの応答性を測定します。

INPとFIDの違い

項目 FID(旧指標) INP(現行指標)
測定対象 最初のインタラクションのみ すべてのインタラクション
測定範囲 入力遅延のみ 入力遅延 + 処理時間 + 表示遅延
代表値 最初の1回の値 98パーセンタイル値

INPの構成要素

INPは以下の3つのフェーズで構成されます。

sequenceDiagram
    participant User as ユーザー
    participant Browser as ブラウザ
    participant Main as メインスレッド
    participant Screen as 画面

    User->>Browser: クリック/タップ/キー入力
    Note over Browser,Main: 入力遅延<br/>(Input Delay)
    Browser->>Main: イベントハンドラ実行開始
    Note over Main: 処理時間<br/>(Processing Time)
    Main->>Screen: レンダリング開始
    Note over Main,Screen: 表示遅延<br/>(Presentation Delay)
    Screen->>User: 視覚的フィードバック

INPの評価基準

スコア 評価 ユーザー体験
200ミリ秒以下 良好(緑) 即座に反応していると感じる
200ミリ秒〜500ミリ秒 改善が必要(オレンジ) やや遅れを感じる
500ミリ秒超 不良(赤) 操作が効いていないと感じる

INP改善の具体的な手法

1. 長時間タスクの分割

メインスレッドをブロックする長時間タスクを分割して、ユーザー入力に素早く応答できるようにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 悪い例: 長時間タスクがメインスレッドをブロック
function processLargeData(items) {
  items.forEach(item => {
    // 重い処理
    heavyComputation(item);
  });
}

// 良い例: タスクを分割して処理
async function processLargeDataOptimized(items) {
  const CHUNK_SIZE = 100;
  
  for (let i = 0; i < items.length; i += CHUNK_SIZE) {
    const chunk = items.slice(i, i + CHUNK_SIZE);
    
    // 各チャンクの処理
    chunk.forEach(item => heavyComputation(item));
    
    // ブラウザに制御を返す
    await yieldToMain();
  }
}

// メインスレッドに制御を返すユーティリティ
function yieldToMain() {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  });
}

2. scheduler.yield()の活用

ブラウザのscheduler.yield()APIを使用して、より効率的にメインスレッドに制御を返すことができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
async function processWithYield(items) {
  for (const item of items) {
    // 処理を実行
    processItem(item);
    
    // scheduler.yield()が利用可能な場合は使用
    if ('scheduler' in window && 'yield' in scheduler) {
      await scheduler.yield();
    } else {
      // フォールバック
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
}

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

不要な処理を削減し、イベントハンドラを軽量に保ちます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 悪い例: 重い処理をクリックハンドラ内で実行
button.addEventListener('click', () => {
  // 即座に視覚的フィードバックを提供しない
  const result = expensiveCalculation();
  updateUI(result);
});

// 良い例: 視覚的フィードバックを先に、重い処理は後で
button.addEventListener('click', () => {
  // 即座に視覚的フィードバックを提供
  button.classList.add('loading');
  button.disabled = true;
  
  // 重い処理は次のフレームで実行
  requestAnimationFrame(() => {
    requestAnimationFrame(async () => {
      const result = await expensiveCalculation();
      updateUI(result);
      button.classList.remove('loading');
      button.disabled = false;
    });
  });
});

4. Web Workerの活用

CPU負荷の高い処理をメインスレッドから分離します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// main.js
const worker = new Worker('worker.js');

button.addEventListener('click', () => {
  // 視覚的フィードバックを即座に表示
  showLoadingIndicator();
  
  // 重い処理をWeb Workerに委譲
  worker.postMessage({ type: 'PROCESS_DATA', data: largeDataSet });
});

worker.addEventListener('message', (event) => {
  const { result } = event.data;
  updateUI(result);
  hideLoadingIndicator();
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// worker.js
self.addEventListener('message', (event) => {
  const { type, data } = event.data;
  
  if (type === 'PROCESS_DATA') {
    // メインスレッドに影響を与えずに重い処理を実行
    const result = expensiveComputation(data);
    self.postMessage({ result });
  }
});

CLS(Cumulative Layout Shift)の詳細と改善方法

CLSは、ページ読み込み中およびページのライフサイクル全体を通じて発生する予期しないレイアウトシフトを測定する指標です。

CLSの計算方法

CLSは、影響を受ける領域(Impact Fraction)と移動距離(Distance Fraction)の積で計算されます。

レイアウトシフトスコア = Impact Fraction × Distance Fraction
graph TD
    subgraph レイアウトシフトの発生
        A[要素が移動] --> B[Impact Fraction<br/>ビューポートの何%が影響を受けたか]
        A --> C[Distance Fraction<br/>要素が何%移動したか]
        B --> D[CLS Score = Impact × Distance]
        C --> D
    end

CLSの評価基準

スコア 評価 ユーザー体験
0.1以下 良好(緑) レイアウトが安定している
0.1〜0.25 改善が必要(オレンジ) 時々ずれを感じる
0.25超 不良(赤) 頻繁にレイアウトがずれる

CLS改善の具体的な手法

1. 画像とメディアのサイズ指定

画像や動画要素には必ずwidthheight属性を指定し、アスペクト比を維持します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- 悪い例: サイズ指定なし -->
<img src="photo.jpg" alt="写真">

<!-- 良い例: サイズを明示的に指定 -->
<img src="photo.jpg" alt="写真" width="800" height="600">

<!-- CSSでアスペクト比を維持 -->
<style>
img {
  max-width: 100%;
  height: auto;
}
</style>

2. aspect-ratioプロパティの活用

CSSのaspect-ratioプロパティを使用して、コンテナのアスペクト比を事前に確保します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/* 動画コンテナ(16:9) */
.video-container {
  aspect-ratio: 16 / 9;
  width: 100%;
  background-color: #f0f0f0;
}

/* 画像プレースホルダー(4:3) */
.image-placeholder {
  aspect-ratio: 4 / 3;
  width: 100%;
  background-color: #e0e0e0;
}

/* 正方形のサムネイル */
.thumbnail {
  aspect-ratio: 1 / 1;
  width: 100px;
  object-fit: cover;
}

3. 広告・埋め込みコンテンツのスペース確保

動的に読み込まれる広告やiframeのスペースを事前に確保します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!-- 広告スロット用のコンテナ -->
<div class="ad-slot" style="min-height: 250px; min-width: 300px;">
  <!-- 広告が読み込まれる -->
</div>

<style>
.ad-slot {
  /* フォールバック表示 */
  background-color: #f5f5f5;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ad-slot:empty::before {
  content: "広告";
  color: #999;
}
</style>

4. Webフォントによるレイアウトシフトの防止

Webフォントの読み込みによるテキストのちらつきを防止します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* フォントのプリロード */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap; /* または optional */
  /* size-adjust でメトリクスを調整 */
  size-adjust: 100%;
  ascent-override: 90%;
  descent-override: 20%;
}

/* フォールバックフォントとのサイズ調整 */
body {
  font-family: 'CustomFont', -apple-system, BlinkMacSystemFont, sans-serif;
}
1
2
3
4
5
6
7
8
9
<head>
  <!-- フォントのプリロード -->
  <link 
    rel="preload" 
    as="font" 
    href="/fonts/custom.woff2" 
    type="font/woff2" 
    crossorigin>
</head>

5. 動的コンテンツの挿入位置の工夫

既存コンテンツの上に新しい要素を挿入しないようにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 悪い例: 既存コンテンツの上に挿入
function addNotification(message) {
  const notification = document.createElement('div');
  notification.textContent = message;
  document.body.insertBefore(notification, document.body.firstChild);
}

// 良い例: 固定位置に表示
function addNotificationOptimized(message) {
  const notification = document.createElement('div');
  notification.className = 'notification';
  notification.textContent = message;
  document.body.appendChild(notification);
}
1
2
3
4
5
6
7
.notification {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 1000;
  /* レイアウトに影響を与えない */
}

Lighthouseによるパフォーマンス計測

Lighthouseは、Googleが提供するオープンソースの自動化ツールで、Webページの品質を測定できます。

Lighthouseの実行方法

Chrome DevToolsから実行

  1. Chrome DevToolsを開く(F12またはCmd/Ctrl + Shift + I)
  2. 「Lighthouse」タブを選択
  3. 計測するカテゴリを選択(Performance、Accessibility等)
  4. 「Analyze page load」をクリック

コマンドラインから実行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Lighthouse CLIのインストール
npm install -g lighthouse

# 基本的な実行
lighthouse https://example.com --output html --output-path report.html

# モバイルエミュレーションで実行
lighthouse https://example.com --preset=perf --form-factor=mobile

# デスクトップモードで実行
lighthouse https://example.com --preset=desktop

Lighthouseのパフォーマンススコア構成(v10以降)

指標 重み 説明
First Contentful Paint(FCP) 10% 最初のコンテンツが表示されるまでの時間
Speed Index 10% コンテンツが視覚的に表示される速度
Largest Contentful Paint(LCP) 25% 最大のコンテンツが表示されるまでの時間
Total Blocking Time(TBT) 30% メインスレッドがブロックされた合計時間
Cumulative Layout Shift(CLS) 25% レイアウトシフトの累積スコア

Lighthouseレポートの読み方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Lighthouseの出力例(JSON形式)
{
  "categories": {
    "performance": {
      "score": 0.85,  // 85点
      "auditRefs": [
        { "id": "largest-contentful-paint", "weight": 25 },
        { "id": "total-blocking-time", "weight": 30 },
        { "id": "cumulative-layout-shift", "weight": 25 }
      ]
    }
  },
  "audits": {
    "largest-contentful-paint": {
      "score": 0.92,
      "numericValue": 1850,  // 1.85秒
      "displayValue": "1.9 s"
    }
  }
}

PageSpeed Insightsによるパフォーマンス計測

PageSpeed Insightsは、ラボデータ(Lighthouse)とフィールドデータ(CrUX)の両方を提供するツールです。

ラボデータとフィールドデータの違い

項目 ラボデータ フィールドデータ
データソース Lighthouse(シミュレーション) Chrome User Experience Report
測定環境 固定された条件 実際のユーザー環境
用途 開発・デバッグ 実際のユーザー体験の把握
更新頻度 リアルタイム 28日間のローリングデータ

PageSpeed Insights APIの活用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// PageSpeed Insights APIの呼び出し例
async function getPageSpeedInsights(url) {
  const apiKey = 'YOUR_API_KEY';
  const apiUrl = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent(url)}&key=${apiKey}&category=performance&strategy=mobile`;
  
  const response = await fetch(apiUrl);
  const data = await response.json();
  
  // Core Web Vitalsの抽出
  const metrics = {
    lcp: data.loadingExperience?.metrics?.LARGEST_CONTENTFUL_PAINT_MS,
    inp: data.loadingExperience?.metrics?.INTERACTION_TO_NEXT_PAINT,
    cls: data.loadingExperience?.metrics?.CUMULATIVE_LAYOUT_SHIFT_SCORE
  };
  
  return metrics;
}

Critical CSSによる初期表示の高速化

Critical CSSとは、ファーストビューの表示に必要な最小限のCSSをインライン化する手法です。

Critical CSSの抽出と適用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  
  <!-- Critical CSSをインライン化 -->
  <style>
    /* ファーストビューに必要な最小限のCSS */
    body { margin: 0; font-family: sans-serif; }
    .header { background: #333; color: white; padding: 1rem; }
    .hero { height: 60vh; display: flex; align-items: center; }
    .hero-title { font-size: 2.5rem; margin: 0; }
  </style>
  
  <!-- 残りのCSSは非同期で読み込み -->
  <link rel="preload" href="/css/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/css/main.css"></noscript>
</head>
<body>
  <header class="header">サイトヘッダー</header>
  <section class="hero">
    <h1 class="hero-title">メインタイトル</h1>
  </section>
</body>
</html>

Critical CSSの自動抽出

Criticalライブラリを使用して、Critical CSSを自動的に抽出できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// critical.config.js
const critical = require('critical');

critical.generate({
  base: 'dist/',
  src: 'index.html',
  target: {
    html: 'index-critical.html',
    css: 'critical.css'
  },
  width: 1300,
  height: 900,
  inline: true,
  extract: true
});

遅延読み込み(Lazy Loading)の実装

ビューポート外のコンテンツを遅延読み込みすることで、初期読み込み時間を短縮できます。

ネイティブLazy Loading

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<!-- 画像の遅延読み込み -->
<img 
  src="image.jpg" 
  alt="説明" 
  loading="lazy"
  width="800" 
  height="600">

<!-- iframeの遅延読み込み -->
<iframe 
  src="https://www.youtube.com/embed/VIDEO_ID" 
  loading="lazy"
  width="560" 
  height="315">
</iframe>

Intersection Observerを使った高度な遅延読み込み

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Intersection Observerによる遅延読み込み
class LazyLoader {
  constructor(options = {}) {
    this.options = {
      root: null,
      rootMargin: '50px 0px',
      threshold: 0.01,
      ...options
    };
    
    this.observer = new IntersectionObserver(
      this.handleIntersection.bind(this),
      this.options
    );
  }
  
  observe(elements) {
    elements.forEach(el => this.observer.observe(el));
  }
  
  handleIntersection(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        this.loadElement(entry.target);
        this.observer.unobserve(entry.target);
      }
    });
  }
  
  loadElement(element) {
    const src = element.dataset.src;
    const srcset = element.dataset.srcset;
    
    if (src) element.src = src;
    if (srcset) element.srcset = srcset;
    
    element.classList.add('loaded');
  }
}

// 使用例
const lazyLoader = new LazyLoader();
const lazyImages = document.querySelectorAll('img[data-src]');
lazyLoader.observe(lazyImages);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!-- 遅延読み込み用のマークアップ -->
<img 
  data-src="large-image.jpg"
  data-srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
  alt="遅延読み込み画像"
  width="1200"
  height="800"
  class="lazy">

<style>
.lazy {
  opacity: 0;
  transition: opacity 0.3s;
}
.lazy.loaded {
  opacity: 1;
}
</style>

web-vitalsライブラリによる計測

Googleが提供するweb-vitalsライブラリを使用して、実際のユーザー環境でCore Web Vitalsを計測できます。

web-vitalsの導入と基本的な使用方法

1
2
# インストール
npm install web-vitals
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';

// 計測結果をアナリティクスに送信
function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,  // 'good', 'needs-improvement', 'poor'
    delta: metric.delta,
    id: metric.id,
    navigationType: metric.navigationType
  });
  
  // Beacon APIで送信(ページ離脱時も確実に送信)
  if (navigator.sendBeacon) {
    navigator.sendBeacon('/analytics', body);
  } else {
    fetch('/analytics', {
      body,
      method: 'POST',
      keepalive: true
    });
  }
}

// 各指標を監視
onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);

アトリビューション情報の取得

より詳細なデバッグ情報を取得するには、アトリビューションビルドを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { onLCP, onINP, onCLS } from 'web-vitals/attribution';

onLCP((metric) => {
  console.log('LCP:', metric.value);
  console.log('LCP要素:', metric.attribution.element);
  console.log('リソースURL:', metric.attribution.url);
  console.log('TTFB:', metric.attribution.timeToFirstByte);
});

onINP((metric) => {
  console.log('INP:', metric.value);
  console.log('インタラクション対象:', metric.attribution.interactionTarget);
  console.log('インタラクションタイプ:', metric.attribution.interactionType);
  console.log('入力遅延:', metric.attribution.inputDelay);
  console.log('処理時間:', metric.attribution.processingDuration);
  console.log('表示遅延:', metric.attribution.presentationDelay);
});

onCLS((metric) => {
  console.log('CLS:', metric.value);
  console.log('最大シフト要素:', metric.attribution.largestShiftTarget);
  console.log('最大シフト時間:', metric.attribution.largestShiftTime);
});

パフォーマンス改善のベストプラクティス

チェックリスト形式のまとめ

LCP改善チェックリスト

  • サーバーレスポンス時間(TTFB)が600ms以下
  • LCP画像がプリロードされている
  • 画像がWebP/AVIFフォーマットで配信されている
  • レンダリングブロックリソースが最小化されている
  • CDNを使用している

INP改善チェックリスト

  • 長時間タスク(50ms超)が分割されている
  • イベントハンドラが軽量化されている
  • 重い処理がWeb Workerに移行されている
  • サードパーティスクリプトが最適化されている
  • 不要なJavaScriptが削除されている

CLS改善チェックリスト

  • すべての画像にwidth/height属性がある
  • Webフォントのfont-displayが設定されている
  • 広告スロットのサイズが事前確保されている
  • 動的コンテンツが既存コンテンツを押し下げない
  • アニメーションがtransformを使用している

パフォーマンス監視の継続的な実施

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// パフォーマンスバジェットの設定例
const performanceBudget = {
  lcp: 2500,    // 2.5秒
  inp: 200,     // 200ms
  cls: 0.1,     // 0.1
  fcp: 1800,    // 1.8秒
  ttfb: 600     // 600ms
};

// バジェット超過時のアラート
function checkBudget(metric) {
  const budget = performanceBudget[metric.name.toLowerCase()];
  if (budget && metric.value > budget) {
    console.warn(`パフォーマンスバジェット超過: ${metric.name} = ${metric.value} (上限: ${budget})`);
    // Slackやメールへの通知
    notifyTeam(metric);
  }
}

まとめ

Core Web Vitalsは、Webパフォーマンスを体系的に評価・改善するための重要な指標です。本記事で解説した内容を実践することで、ユーザー体験とSEOの両面で成果を上げることができます。

パフォーマンス最適化は一度行えば終わりではなく、継続的な計測と改善が重要です。LighthouseやPageSpeed Insights、web-vitalsライブラリを活用して定期的に計測を行い、問題が発生した際に迅速に対応できる体制を整えましょう。

参考リンク