gazou-compressor.jp

画像Lazy Loadの閾値設計:rootMargin/優先度/INPを両立する最短ガイド

“lazy を付ければ速くなる” は半分正しく半分誤りです。閾値(rootMargin)や優先度を誤ると LCP 候補が遅延し、逆に体感が悪化します。本稿は ①判定 ②閾値 ③優先度 ④検証 を 5 分で確定する最小フレームを提示し、調整の属人性を排除します。

先に結論
ヒーロー/LCP候補=非lazy + priority + fetchpriority=high。折りたたみ直下= rootMargin 400px。長い記事本文サムネ= 600px 以上まとめ読み。

要点(TL;DR)

1. 背景:なぜ “lazy全部” が失敗するか

近年ブラウザデフォルト lazy(Chrome)はビューポート近傍のみ先行読み込みします。Hero にまで lazy を付与すると HTTP リクエストが遅延し LCP が伸長。逆に rootMargin を極端に大きくすると eager と変わらない帯域先行で INP が下がるケースもあります。

2. 分類フロー

  1. Hero/LCP候補を列挙し非lazy化
  2. 初期 viewport + 1スクロール以内 = fold直下
  3. 連続サムネ (記事一覧/ギャラリ) をブロック単位で遅延
  4. 残りは lazy + 大きめ rootMargin (本文末尾)
  5. 検証: Web Vitals ログ / DevTools waterfall

3. rootMargin プリセット

対象rootMargin理由
fold直下(1画面先)400px高速スクロールでも白抜け防止
本文中段サムネ600px連続表示の先行フェッチ
本文末尾/低優先800px視認遅め。先行し過ぎても影響軽微

4. 実装スニペット

// observer.ts
export function createVisibilityObserver(opts: { rootMargin: string, cb: (el: HTMLElement)=>void }) {
  const io = new IntersectionObserver((entries) => {
    for (const e of entries) if (e.isIntersecting) { cb(e.target as HTMLElement); io.unobserve(e.target); }
  }, { rootMargin: opts.rootMargin });
  return { observe: (el: HTMLElement) => io.observe(el) };
}

コールバックでは requestIdleCallback で decode を遅らせ主スレッドブロックを回避。

5. 優先度ヒント戦略

6. INPを悪化させない工夫

大量画像で IO コールバックが集中すると main thread が詰まり入力遅延が跳ねます。バッチ化(setTimeout 0 / idle)と decode 非同期化 (HTMLImageElement.decode) を活用。

7. 公開前チェック(7項目)

8. まとめ

“分類 → rootMargin プリセット → 優先度ヒント最小化 → INP保護” の順に固定すると迷いが消えます。過剰なカスタムロードよりまず基本4段階を再現性高く運用してください。

gazou-compressor.jp 編集部

画像圧縮・変換・背景除去などの実践テクニックと、Webで“速く・軽く・崩さない”ためのノウハウを発信しています。

関連記事

トピック/更新日の近いコンテンツ