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 ランキングにも影響します。
| 指標 | 意味 | 良好 | 要改善 |
|---|---|---|---|
| LCP | Largest Contentful Paint(最大コンテンツ描画) | ≤ 2.5s | > 4.0s |
| INP | Interaction to Next Paint(インタラクション応答) | ≤ 200ms | > 500ms |
| CLS | Cumulative 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 の「フィールドデータ」で実ユーザーの体験を確認することが大切です。