Gojabako ZoneKei Ito

next/imageが遅いのでやめた

に公開に更新)履歴 (6)

iPadケースを作った記事の画像が読み込みに2秒以上かかることがあり閲覧に支障があったため改善したまとめです。

修正前の画像読み込みの具合
修正前の画像読み込みの具合

next/imageは画像を調整してくれるのはいいのですが、元の画像が3MBくらいでキャッシュがないと表示まで3秒くらいかかっていました。ここは私の個人サイトなので編集がなければコンテンツは変わらないので、事前にいい具合のサイズの画像を生成しておいてブラウザに選択させるようにしました。実装した機能は以下の通りです。

  1. srcとpublicの画像を列挙する
  2. それぞれについて 幅(300, 400, ..., 1800) × 形式(オリジナル, webp, avif) の画像を生成する
    • 例えば 幅(300, 400, 800) × 形式(jpeg, webp, avif) なら9種類
  3. 生成した画像の一覧を使って {元ファイル}.component.tsx を生成する(このファイルは.gitignoreする)
  4. 画像がすでに生成してあれば省略する(.gitignoreしてるのでコンポーネントの生成は毎度やる)

{元ファイル}.component.tsxは例えばこんな感じです。

生成されるコンポーネントの例tsx
1import type {DetailedHTMLProps, ImgHTMLAttributes} from 'react';2const Image = (3 props: DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>,4) => <picture style={{aspectRatio: '1526/1266'}}>5 <source srcSet="/images/v1/Qb2kzaQ4/300w.webp 300w, /images/v1/Qb2kzaQ4/400w.webp 400w, /images/v1/Qb2kzaQ4/500w.webp 500w, /images/v1/Qb2kzaQ4/600w.webp 600w, /images/v1/Qb2kzaQ4/800w.webp 800w, /images/v1/Qb2kzaQ4/1000w.webp 1000w, /images/v1/Qb2kzaQ4/1200w.webp 1200w, /images/v1/Qb2kzaQ4/1500w.webp 1500w" type="image/webp" />6 <source srcSet="/images/v1/Qb2kzaQ4/300w.avif 300w, /images/v1/Qb2kzaQ4/400w.avif 400w, /images/v1/Qb2kzaQ4/500w.avif 500w, /images/v1/Qb2kzaQ4/600w.avif 600w, /images/v1/Qb2kzaQ4/800w.avif 800w, /images/v1/Qb2kzaQ4/1000w.avif 1000w, /images/v1/Qb2kzaQ4/1200w.avif 1200w, /images/v1/Qb2kzaQ4/1500w.avif 1500w" type="image/avif" />7 <img alt="" {...props} srcSet="/images/v1/Qb2kzaQ4/300w.png 300w, /images/v1/Qb2kzaQ4/400w.png 400w, /images/v1/Qb2kzaQ4/500w.png 500w, /images/v1/Qb2kzaQ4/600w.png 600w, /images/v1/Qb2kzaQ4/800w.png 800w, /images/v1/Qb2kzaQ4/1000w.png 1000w, /images/v1/Qb2kzaQ4/1200w.png 1200w, /images/v1/Qb2kzaQ4/1500w.png 1500w" />8</picture>;9export default Image;

この対策により表示にかかる時間(の最大値)は3,000msから200ms以下になりました。

修正後の画像読み込みの具合
修正後の画像読み込みの具合

訂正

このツイートの50msはおそらくx-vercel-cache: HITのを見ているので正しくないです。

複数サイズのwebpとavifを生成しておいて<picture>と<source>と<img>で出すようにした
→ 50ms以内で出てるので目標は達成
画像読み込み完了時の描画ずれは<picture>へのaspect-ratioで対応できたけどスクロールした状態で再読み込みすると数pxずれる pic.twitter.com/aOyLrcEfW7

— Kei Ito (@gjbkz) February 5, 2022

参考文献

  1. MDN Web Docs
    1. <picture>: The Picture element
    2. <source>: The Media or Image Source element
    3. <img>: The Image Embed element