Nuxt3 img v-bind:srcで画像が表示されない時の解決法
imgタグで動的srcを使おうとしたときに画像が表示されなくて困りました。
解決法を見つけたので紹介します。
新たな解決方法(2022/8/1追記)
この度kensukeさんよりコメントを頂き、
再検証を行う過程で先に紹介していたものより
簡潔で楽な解決方法を見つけましたのでご紹介しようと思います。
<script setup lang="ts">
type Item = { id: string, name: string }
const items: Array<Item> = [
{ id: "1", name: "sample1" },
{ id: "2", name: "sample2" }
]
</script>
<template>
<template v-for="item in items">
<img :src="`../assets/images/${item.name}.png`" />
</template>
</template>
静的srcでは@/assets/images/~~みたいに書いても動きますが、
動的srcではこれでは動かず、../assets/imagesと書く必要があるみたいです!
結論。これで解決(古い解決方法です)
new URLを使って、動的srcを変換する方法で解決しました。
<script setup lang="ts">
//imgのpathを生成する関数
const generateImgPath = (fileName: string): string => {
return new URL(`../assets/images/${fileName}.png`, import.meta.url).href
}
</script>
<template>
<template v-for="items in items">
<img :src="generateImgPath(item.name)" :alt="item.name">
</template>
</template>
const generateImgPath = (path: string): string => {
return new URL(path, import.meta.url).href
}
この記事読んでやっと解決しました!
(Nuxt3はデフォルトでviteを使っているので、Viteのドキュメントを読む必要がありました)
ビハインドストーリー
静的srcでは問題なく動いていた。
<template>
<img src="@/assets/images/sample.png"
</template>
これをv-bindを使って変数を指定したところ、画像が表示されなくなった(汗)
<template v-for="item in items">
<img :src='`@/assets/images/${item.name}.png`'>
</template>
ブラウザでHTMLを確認すると、srcが文字列のままで表示されていた。これだとURLが正しくないので画像が読み込まれるはずがない。
<img src="@/assets/images/sample.png">
本来はこうなってなっているべき
<img src="http://localhost:3000/_nuxt/assets/images/sample.png" >
解決策を探したところ、requireを使えばいいとのことで、以下コードに直したが、
ページが真っ白になり、コンソールにはrequire is not definedと出ていた。
<template v-for="item in items">
<img :src="require('@/assets/images/${item.name}.png')"
</template>
この記事によるとViteだとrequireは動かないらしい。
(Nuxt3はデフォルトでviteが使われている)
この記事をみて、new URLを使ったところローカルでは無事動くようになった!!!
その時のコード↓
<script setup lang="ts">
//動的srcをvueに認識させるための変換ツール
const convertImgSrc = (src: string): string => {
return new URL(src, import.meta.url).href
}
</script>
<template>
<template v-for="items in items">
<img :src="convertImgSrc(`../assets/images/${item.name}.png`)" :alt="item.name">
</template>
</template>
でも、、GitHub Pagesにデプロイしたところ、エラーが出て画像が表示されない。。
Failed to construct 'URL': Invalid URL
諦めかけた時に、偶然この記事を見つけて、
new URLの第一引数に変数を入れると動かないことに気付き、
以下コード(冒頭で紹介したコード)に直したところ、ローカルでも本番環境でも無事動いた!
<script setup lang="ts">
//imgのpathを生成する関数
const generateImgPath = (fileName: string): string => {
return new URL(`../assets/images/${fileName}.png`, import.meta.url).href
}
</script>
<template>
<template v-for="items in items">
<img :src="generateImgPath(item.name)" :alt="item.name">
</template>
</template>
imageの実装でこんなに詰まると思っていなかったけど、無事解決して良かったです。
最近公式ドキュメントををしっかり読むことで解決するケースが多いので、公式のありがたみを感じています><
同じ問題で困っている人のお役にたてたら幸いです!
Discussion
Lazyをコンポーネントの前につけることで解決できるかもしれません。(dynamic imports)
→こちらではうまくいかないことがわかりました。すみません。
kensukeさん
コメントありがとうございます!
画像表示に使っているのがコンポーネントではなく、HTMLの標準imgタグではありますが、
試しに<lazy-img>、<Lazy-img>、<LazyImg>などにタグ名を変えて試してみましたが、
imgタグとして認識されなくなり、問題の解決には至りませんでした。
(処理を別コンポーネントに移し、<LazyXXX>みたいに使ってみましたが結果は変わらず..)
ただ、お陰様で今回再検証行った結果、より簡単な解決方法を見つけることが出来ました!
失敗コード:(path先頭に@を使用)
成功コード(path先頭に..を使用)
ありがとうございます^^記事に追記させて頂きます!
こちらの場合では、build時にうまく表示されないケースがあるかもしれないのですが、適切にbuildされましたでしょうか。(上記で誤りのあるコメントをし、すみません。)
@basicactor
試してみましたが、dev起動で確認できても、buildではうまく表示されないようです。
buildの場合、
new URL('画像相対パス', import.meta.url)
にしないとだめでした。@も使えず。
また、
nuxt@3.0.0-rc.6
はバグが有るようで、動的URLはどうやってもうまくいきませんでした。下記で検証しました。
コメントの
test00:ooo
は、1番目:
yarn dev -o
で確認2番目:buildしてFirebaseエミュレータで確認(nuxt@3.0.0-rc.6版)
3番目:buildしてFirebaseエミュレータで確認(npm:nuxt3@latest版, 3.0.0-rc.6-27669113.5aa7288)
rc.6で失敗しているのは、
__NUXT_BASE__
を/_nuxt/
に置換できていないのが原因のようです。どうもデグレしている模様。
Viteのバージョンで変わっているようですが、下記で動きました。