🖼️
Astroで、ローカルの画像を最適化する
ローカルにある画像を(動的に)最適化するのは難しい
importを使えない状況だってある
通常、Astroでローカルの画像を最適化する場合、import
であらかじめ画像を静的に指定しておく必要があります。いつも画像の場所が一意に定まっているのであればいいのですが、動的に取得した画像を最適化したいこともあるかと思います。
ですが、astro:assets
でローカルにある画像を扱うのは意外と難しいです。例えば、以下のようなコードで画像を最適化しようとしても、出力されるのは最適化前の画像です。ファイルパスをstring
で渡すような使い方は、astro:assets
ではできないのです。
❌ 外部からjpgを渡して、最適化したい…
---
import {Picture} from "astro:assets";
interface Props{
image: string; // スクリプトの呼出元から画像のファイルパスが渡される
}
const {image} = Astro.props;
---
<Picture
src={image}
alt="AVIF/WEBPに変換します"
height="128"
width="256"
formats={['avif', 'webp']}
/>
// ↑ 表示されるのは、呼出元から渡されたjpgのみ
公式のドキュメントにも、ローカル画像はimport
すべしとあります。
それでもローカル画像を動的に最適化したい
Astro.globを使おう
力技ではありますが、Astro.glob (import.meta.glob)
で条件に合う画像を部取得 → 与えられたファイルパス(string
)に合致する画像を抽出する操作で解決します。
再利用性を考えて、画像抽出操作をするスクリプトを、.astro
ファイルとは別に用意します。
✅ image_optimizer.ts
// /public/images/内の画像を取得
const localImages = await import.meta.glob('/public/images/**/*', { eager: true });
// 与えられたファイルパスと一致する画像があれば、その画像をオブジェクトとして返す
export async function getImageObject(filePath: string) {
const imageModule = localImages['/public' + filePath];
if (imageModule) {
return await (imageModule as any).default;
}
else {
// エラーの際の処理
throw new Error(`Image not found: ${filePath}`);
}
}
まずは、上のスクリプトを指定し、.astro
ファイルからgetImageObject()
関数をインポートします。
そして、getImageObject()
関数を実行して取得した画像を、<Picture>
に放り込んでみましょう。今度は無事に最適化されるはずです。
✅ index.astro
---
import {getImageObject} from "./image_optimizer";
interface Props{
image: string; // スクリプトの呼出元から画像のファイルパスが渡される
}
const {image} = Astro.props;
const imageObj = getImageObject(image);
---
<Picture
src={imageObj}
alt="AVIF/WEBPに変換します"
height="128"
width="256"
formats={['avif', 'webp']}
/>
// ↑ 今度は、avifとwebpに変換された画像が表示されるはずです。
import.meta.glob('/public/images/**/*', { eager: true });
で画像を全取得するのがパフォーマンス的に少し気になりますが、小規模なウェブサイトであればほぼ問題ないかとは思います。
Discussion