INP時代の画像遅延読み込み:lazy/priority/fetchpriority/sizesの最適解
画像の遅延読み込みはネットワークを節約しますが、やり方を誤るとINP(入力応答)やLCPを悪化させます。鍵は“ヒーローだけは即時”、“折り返し以降は遅延”、 そして“描画幅に合った配信”の3点です。本記事は最短で運用に落とせるルールと実装を提示します。
先に結論(最短フロー)
- LCP候補(ヒーロー)はlazy禁止+
fetchpriority="high"
+preload
。 - 折り返し以降は
loading="lazy"
。IntersectionObserverで1〜1.5画面先を先読み。 srcset/sizes
を実表示幅に一致(関連: srcset/sizes)。- すべての
<img>
に width/height を指定し、 CLSゼロ設計 と両立。
要点(TL;DR)
- ヒーロー(LCP)はpreload + fetchpriority="high"、lazy禁止。
- 折り返し以降はIntersectionObserverで先読み幅を制御(rootMargin 100–150%)。
sizes
は描画幅、srcset
は候補解像度。過大DLはINP/LCP双方に悪影響。- 全画像に寸法(width/height)を入れて、読み込み後の押し下げ(CLS)を防止。
1. なぜ“lazyだけ”ではダメなのか
重要画像まで lazy にすると、ユーザー操作直後のペイントが遅れINPの悪化に繋がります。 逆に何でも eager にするとネットワーク飽和でLCP悪化。優先度の切り分けが肝心です。
2. 最短フロー(実務)
- ヒーロー(LCP)を特定し、preload + fetchpriority="high" を付与。
- 折り返し以降の画像は
loading="lazy"
。IOで1〜1.5画面先の先読み。 srcset/sizes
を描画幅に合わせ、過大DLを阻止。- 全画像に width/height を必ず入れる(CLSゼロ運用)。
3. 実装レシピ(コピペOK)
3.1 ヒーロー(LCP)
// HTML<!-- ヒーロー(LCP候補)は lazy禁止。fetchpriority="high" + 適切な sizes -->
<link
rel="preload"
as="image"
href="/hero-1200.avif"
imagesrcset="/hero-800.avif 800w, /hero-1200.avif 1200w, /hero-1600.avif 1600w"
imagesizes="(min-width: 1024px) 1000px, 92vw"
/>
<img
src="/hero-1200.avif"
srcset="/hero-800.avif 800w, /hero-1200.avif 1200w, /hero-1600.avif 1600w"
sizes="(min-width: 1024px) 1000px, 92vw"
width="1600" height="900"
alt="ヒーロー"
fetchpriority="high"
decoding="async"
/>
3.2 カード一覧(lazy)
// HTML<!-- 折り返し以降のカード画像は lazy + 適切な sizes -->
<img
src="/thumb-640.webp"
srcset="/thumb-320.webp 320w, /thumb-480.webp 480w, /thumb-640.webp 640w"
sizes="(min-width: 1024px) 320px, (min-width: 640px) 33vw, 45vw"
width="640" height="400"
alt="カード"
loading="lazy"
decoding="async"
/>
3.3 IntersectionObserverで先読み幅調整
// JavaScript// IntersectionObserverで「スクロール1〜1.5画面先」を先読み
const io = new IntersectionObserver((entries) => {
for (const e of entries) {
if (e.isIntersecting) {
const img = e.target as HTMLImageElement;
// data- 属性から本来のsrc/srcsetに差し替え
if (img.dataset.src) img.src = img.dataset.src;
if (img.dataset.srcset) img.srcset = img.dataset.srcset;
io.unobserve(img);
}
}
}, { rootMargin: "150% 0px" }); // 1.5画面先で事前ロード
document.querySelectorAll("img[data-src]").forEach((el) => io.observe(el));
3.4 content-visibilityで描画負荷を削減
// CSS/* 画像が多い長文記事に:非表示領域のレイアウト計算をスキップ */
.article-section {
content-visibility: auto; /* ビューポート外は描画を遅延 */
contain-intrinsic-size: 1px 600px; /* 予測高さでレイアウトを安定 */
}
3.5 優先度ルール(ミニガイド)
// Guide// 画像優先度の判断(最小ルール)
/*
- LCP候補(ビューポート内の最大画像): fetchpriority="high"(lazy禁止)
- ファーストビューの補助画像: fetchpriority="low" か none(状況次第)
- 折り返し以降: loading="lazy"(IOで先読み幅を調整)
- すべての <img> に width/height(CLS対策)
- srcset/sizes は実表示幅に一致(過大DLの回避)
*/
4. 応用とハマりどころ
- カルーセルは初期表示分のみを eager、それ以外は lazy。
- SVGアイコンはインライン化で往復を削減。写真はAVIF/WebPを優先(/compressor で確認)。
- 遅延読み込みの閾値(rootMargin)はコンテンツ密度に合わせて 75〜150% の範囲で調整。
5. 公開前チェック
- LCP候補がlazyになっていない(preload+fetchpriority=high)。
- 折り返し以降は
loading="lazy"
、先読み幅は適切(IOのrootMargin設定)。 srcset/sizes
が実表示幅に一致し、過大ダウンロードが無い。- 全画像が width/height 指定済み(関連:CLSゼロ設計)。
6. まとめ
“ヒーローは即時・それ以外は遅延・描画幅に合わせる”——この3点をテンプレ化すれば、INP/LCP/CLSを同時に改善できます。 実装後は /compare と /compressor で画質・容量を検証し、安定運用に移行しましょう。
FAQ(よくある質問)
画像形式の基本方針は?(写真/スクショ/透過)
写真は AVIF / WebP(画質80–85%目安)、UIやスクショはPNG / WebP Lossless、単色ロゴはSVGが基本です。 実装の詳細は srcset/sizes設計ガイド と スクショ最適化 を参照してください。
圧縮しても画質を落とさないコツは?
実表示幅に合わせたリサイズ → 過大ダウンロードを防ぎ、
srcset/sizes
を 実描画幅に一致させます。画質は写真で 80–85% を起点に、ノイズやエッジを目視確認。 仕上げは /compare で Before/After を見比べるのがおすすめです。