Nuxt 3の画像の動的表示でつまづいたところ
本記事は、mediba Advent Calender2024の7日目の記事になります。
問題
以下のような要件で画像を動的に表示しようとしていました。
- JSONから取得した画像のURLを指定する
- 画像のURLが空or存在しない場合は、デフォルト画像を表示する
- 画像の取得に失敗した場合はエラー画像を表示する
しかしimgタグのsrcをv-bindで動的に設定したところ、画像が表示されなくて困りました。
解決までの備忘を残します。
過程
まず、静的srcでは問題なく表示されました。
<template>
<img src="@/assets/default.png" />
</template>
これをv-bindを使って動的表示にしたところ、画像が表示されなくなりました。
<template>
<img :src="getImageUrl(data)" />
</template>
<script>
const defaultImageUrl = "@/assets/default.png";
function getImageUrl(data) {
return data.image_url && data.image_url.trim() !== ""
? data.image_url
: defaultImageUrl;
}
</script>
ブラウザで確認すると、静的srcでは/_nuxt/assets/default.png
に変換されていたものが、@/assets/default.png
の文字列のままになっていました。
Nuxtで画像を扱う際、src要素を静的に指定した場合はWebpackによりモジュールとして読み込んでくれる一方で、src属性にv-bindを使い動的に指定するとそのままのパスとして扱われるようです。
また、Nuxt2では以下のようにrequire()
を使うことで動的表示が可能でしたが、Nuxt3で使われているViteではrequireをサポートしていないため、この方法は使えません。
<img :src="require(hogehoge)">
解決
方法1:importを使用
今回はデフォルト画像とエラー画像を明示的にインポートすることで解決しました。
import defaultImageUrl from "@/assets/default.png";
import errorImageUrl from "@/assets/failed.png";
画像の数が多い場合はインポートが大変なのでまた検討が必要そうです。
方法2:画像をpublicに配置
publicフォルダに画像を配置すると、ビルド時にパスがそのまま保持されるため、画像を動的に表示することができます。
しかし、publicに置くと画像が最適化されないため、サイズやパフォーマンスの管理が必要です。
余談
今回の要件に必要なエラー画像の表示を@error
を使って実装しようとしたところ、再レンダリングがうまくいかなかったので、最終的にsrcを直接書き換えるのではなく、画像のURLを管理するためのリアクティブなプロパティ(displayImageUrl
)を利用することにしました。
<img :src="data.displayImageUrl" @error="onImageError(data)" />
<script>
// ~~ importなど ~~
const fetchData = async () => {
// ~~ データ取得 ~~
data.value = data.list
.map((list) => ({
...list,
content: list.content.map((data) => ({
...data,
// リアクティブなプロパティを追加
displayImageUrl:
data.image_url && data_url.trim() !== "" ? data.image_url : defaultImageUrl,
})),
}))
};
// エラー画像の表示
const onImageError = (data) => {
data.displayImageUrl = errorImageUrl;
};
</script>
imageの実装で思わぬ落とし穴にハマりました。
同じ事象でお困りの方の参考になれば幸いです。
最後に、現在medibaではメンバーを大募集しています。
募集・応募ページ
medibaってどんな会社だろう? と興味を持っていただいた方は、
カジュアル面談もやっておりますので、お気軽にお申込み頂ければと思います。
Discussion