Vue.jsを使っていると、imgタグのsrc属性で指定する画像のパスを、computedやmethodsなどの処理結果の戻り値や、処理の中で返したいときがあります。
そのときに、computedなどの処理の中で画像の相対パスを指定して、v-bind(:src)として画像のパスを渡しても、パスが狙ったようにコンパイルされず、画像が表示できないことがあります。
ここでは、そんなときに画像をきちんと表示する方法について解説しています。
画像が表示できない処理の例(パス指定ができない)
次のようなコードでは、imgタグのsrcにv-bindで画像のパスが狙ったように渡りません。
<template>
<img :src="imgSrc">
</template>
<script>
export default {
props: {
image: String,
},
computed: {
imgSrc () {
const img = "./images/" + this.image + ".jpg"
return img
},
}
}
</script>
親コンポーネントから渡されたimageというプロパティによって、表示するイメージ画像を切り分けようとする処理です。
img = "./images/" + this.image + ".jpg"
で生成した相対パス(例: ./images/sky.jpg)を
<img :src="imgSrc">
として、imgタグのsrc属性の値に渡そうとする処理です。
コンパイル後にHTMLのソースコードを確認すると、生成したパスがそのまま入ってしまいます。
<img src="./images/sky.jpg">
なお、propsやdataなどのプロパティを使わずに、以下のように指定しても画像は表示されません。
<template>
<img :src="imgSrc">
</template>
<script>
export default {
computed: {
imgSrc () {
return "./images/sky.jpg"
},
}
}
</script>
対処法
対処法はとても簡単です。computedやmethodsなどのVue.jsの中の処理で画像のパスを相対パスで指定するときに、requireメソッドを使って、相対パスを引数に渡します。
require("相対パス")
実例
先ほどのエラーが出た例の場合も、computedプロパティの中の処理で、画像の相対パスをrequireメソッドで囲んで次のようにします。
computed: {
imgSrc () {
return require("./images/" + this.image + ".jpg")
},
こうすることで、templateタグの中でsrc属性とプロパティ「imgSrc」をv-bindしたときに、正しい画像のパスが入ります。
コードの全体像
<template>
<img :src="imgSrc">
</template>
<script>
export default {
props: {
image: String,
},
computed: {
imgSrc () {
return require("./images/" + this.image + ".jpg")
},
}
}
</script>
エラーの原因(なぜそうなるのか?)
Vue.jsはimgタグのsrcで指定されている相対パスをそのまま表示しているわけではありません。vue-loaderというライブラリを使ってコンパイルしてhtmlに変換しています。
vue-loaderは、imgタグのsrc属性やurl( )メソッドなどで指定された相対パスを、require(相対パス)としてコンパイルしています。
つまり、templateタグ内に以下のような記述があるとします。
<img src="../image.png">
vue-loaderはこれを次のようにコンパイルしています。
createElement('img', { attrs: { src: require('../image.png') }})
createElementメソッドを使って、imgタグを生成し、attrsオブジェクトの中で、src属性の値にrequire('../image.png')
を指定しています。
この処理の結果、Vue.jsのプロジェクトの中のimagesディレクトリの場所を示すパスが入り、画像が正しく表示されるようになります。
imgタグのsrc属性をv-bindした場合はこのコンパイル処理が走らなくなります。なので、computedやmethodsなどの処理の中で、requireメソッドを記述しなければいけないとういわけです。
(参考)vue-loader公式 アセットURLハンドリング
補足1:srcに直接パスを指定した実例
imgタグのsrc属性に直接パスを指定すれば、問題なく表示されます。
<template>
<img src="./images/picture.jpg">
</template>
<script>
export default {
props: {
image: String,
}
}
</script>
▼ブラウザのソースコード
<img src="/packs/media/images/picture-f0eae5a3.jpg">
画像のURLが入り、ブラウザできちんと表示されます。
これは、上記の原因のところで解説したようにvue-loaderがコンパイルしてくれているためです。
補足2:絶対パスを指定した場合
絶対パスを指定した場合はコンパイルせずそのまま表示します。(ファイルがない場合はコンパイルエラーになる。)
<template>
<img src="/Users/projects/Modules/images/picture.jpg">
</template>
↓ コンパイル後のブラウザの表示
<img src="/Users/projects/Modules/images/picture.jpg">
このように、絶対パスを指定した場合は、ブラウザでもそのまま表示されます。