next/imageとは?
reactのフレームワークであるNext.jsが標準で提供している画像用のコンポーネント。
画像のパフォーマスを上げる機能が色々と付いており、この中に画像最適化というものがある。
画像最適化とは?
色々ありますが、主に以下が自動で行われる。
- ①webpへの変換
- pngに比べてファイルサイズが半分になる
- 見た目もほぼ変わらない
- ②圧縮してファイルサイズの削減
- quality:0~100で指定可能、値が0に近いほど圧縮される(圧縮される分画質も悪くはなる)
- https://nextjs.org/docs/pages/api-reference/components/image#quality
- ③指定された画像サイズ、またはレスポンシブの場合画面幅によって自動で表示する画像サイズを変更
- 画面サイズが小さいスマホでは小さいサイズの画像、画面サイズが大きいPCでは大きいサイズの画像を表示してくれる。例えばスマホであまりに大きいサイズの画像を表示してしまうと不必要に表示まで時間がかかってしまうため
より詳細は以下記事参照
独自実装した理由
vercelの無料プランだと画像最適化が1000枚までの制限があったため。有料プランだと5,000枚になるが、上げるのは難しったので独自実装できないか?と試してみた。
ref:https://vercel.com/docs/pricing/image-optimization
独自実装した方法
①sharpパッケージで画像変換
画像のリサイズなど様々な処理をできるsharpパッケージを使用する。next/imageも内部的にはこのsharpパッケージを使用している。
今回はnext/imageで行われている以下の処理をする。
- webpへの変換
- qualityによる圧縮
- 各サイズの画像を作成
以下コード例
import sharp from 'sharp'
const deviceSizes = [475, 640, 750, 828, 1080, 1200, 1920]
const imageUrl = 'https://placehold.jp/1920x1920.png'
const response = await axios({
responseType: 'arraybuffer',
url: imageUrl,
})
for (const size of deviceSizes) {
const outputPath = `image-${size}.webp`
const image = await sharp(Buffer.from(response.data))
.resize(size, null, {
fit: 'inside',
})
.webp({
quality: 75,
})
.toFile(outputPath)
}
サイズの配列でループ処理して、sharpのresize関数にサイズを指定して画像サイズを変更。
sharpのwebp関数でwebpに変換。
webp関数のオプションのqualityでnext/image同様に圧縮できる
自分の場合はsharpで変換した画像をaws s3に保存しておき、画像キャッシュもしたいのでcloudfrontを経由して画像を配信しています。
②imgタグのsrc、srcsetにsharpで変換した画像を指定する
画像URLは例です。
<img
src="https://placehold.jp/1920x1920-1920.webp"
srcset="
https://placehold.jp/1920x1920-475.webp 475w,
https://placehold.jp/1920x1920-640.webp 640w,
https://placehold.jp/1920x1920-750.webp 750w,
https://placehold.jp/1920x1920-828.webp 828w,
https://placehold.jp/1920x1920-1080.webp 1080w,
https://placehold.jp/1920x1920-1200.webp 1200w,
https://placehold.jp/1920x1920-1920.webp 1920w
"
sizes="(max-width: 768px) 100vw, 50vw"
loading="lazy"
>
next/imageも内部的にはこのsrcsetが使用されている
レスポンシブ画像の場合はsizesの指定も重要になります。srcsetとsizesの書き方は以下記事を参考にしください
最後に
独自実装してみて、これらを全て自動で行ってくれるnext/imageが便利だなと改めて実感しました。Next.jsを使っておらずnext/imageが使えない場合や、自分と同じように無料プランの1,000枚を越えそうなときにぜひ独自実装をお試しください