Vue.jsでjsonから画像が取得できないときのパスの設定方法


#はじめに
Vue CLI バージョン3で画像をどこのフォルダに入れるかで悩みました。通常のimgタグではsrc/assetsで問題ありませんが、jsonから呼び出すときに画像が表示出来なくなりました。
結論から言うと、publicに入れることで、どこからでも呼び出しができるようになりました。

#assetsに入れる場合
Googleで検索すると、assetsに入れる例がたくさん出てくるので、まずはこの方法で実装していました。

src/App.vue
// src/assets/imagesに画像を格納

// src/App.vue
<template>
  <img src="@/assets/img1.png"/>

  <div v-for="(item, i) in topImages" :key="i">
    <img :src="item.src"/>
  </div>
</template>

<script>
export default {
  data() {
    return {
     topImages: [
       { src: require('@/assets/images/top1.png') },
       { src: require('@/assets/images/top2.png') }
      ]
    }
  }
}
</script>

scriptで取得する場合もrequireを使って呼び出すことで問題なく表示できます。

しかし、この画像パスをjsonファイルに書いて呼び出すことが出来ませんでした。

#jsonに画像パスを書いた場合(失敗1)
問題のコード

src/App.vue
// src/assets/imagesに画像を格納

<template>
  <div v-for="(item, i) in members" :key="i">
    <img :src="item.image_url"/>
  </div>
</template>

<script>
export default {
  data() {
    return {
     members: []
    }
  },
  created() {
    this.axios.get('members.json')
      .then(function (response) {
        this.members = response.data
      }.bind(this)).catch(function (e) {
        console.error(e)
      })
  }
}
</script>
src/members.json
[
  {
    "image_url": "@/assets/images/img1.png",
  }
  {
    "image_url": "@/assets/images/img2.png",
  }
  {
    "image_url": "@/assets/images/img3.png",
  }
]

この状態だと、ブラウザ上に画像が表示されません。(Chromeだと山のマークになる)
検証ツールで確認すると、<img src="@/assets/images/img1.png">になります。
パスではなく、文字列としてそのまま呼ばれてしまっています。

#jsonに画像パスを書いた場合(失敗2)
'@/assets/images/'のみ文字列で固定し、画像名だけ読み込もうとしましたが、こちらはブラウザだけでなく検証ツールにも反映されませんでした。DOMが生成されていない?

src/App.vue
// src/assets/imagesに画像を格納

<template>
  <div v-for="(item, i) in members" :key="i">
    <img :src="'@/assets/images/' + item.image_url"/> // ←変更
  </div>
</template>

<script>
export default {
  data() {
    return {
     members: []
    }
  },
  created() {
    this.axios.get('members.json')
      .then(function (response) {
        this.members = response.data
      }.bind(this)).catch(function (e) {
        console.error(e)
      })
  }
}
</script>
src/members.json
[
  {
    "image_url": "img1.png", // ←変更
  }
  {
    "image_url": "img2.png",
  }
  {
    "image_url": "img3.png",
  }
]

#jsonに画像パスを書いた場合(失敗3)
失敗2と全く同じコードのまま、members.jsonファイルをsrcからpublicフォルダへ移動させました。
この場合は、失敗1と同じ現象が起こり、画像は表示されませんでした。

#jsonに画像パスを書いた場合(成功)
結局、画像もjsonも全てpublicフォルダへ移動することで解決しました。
'images/'だけ文字列で入力し、画像名だけ変数で呼び出すことができました。

public/App.vue
// public/assets/imagesに画像を格納

<template>
  <div v-for="(item, i) in members" :key="i">
    <img :src="'images/' + item.image_url"/> // ←変更
  </div>
</template>

<script>
export default {
  data() {
    return {
     members: []
    }
  },
  created() {
    this.axios.get('members.json')
      .then(function (response) {
        this.members = response.data
      }.bind(this)).catch(function (e) {
        console.error(e)
      })
  }
}
</script>
public/members.json
[
  {
    "image_url": "img1.png",
  }
  {
    "image_url": "img2.png",
  }
  {
    "image_url": "img3.png",
  }
]

#src/assets と public どちらに配置すべきか
###src/assets の特徴
・base64形式で画像が出力されるため、HTTPリクエストの発生を減らすことが出来る。
・webpackでコンパイルできる
・キャッシュされない

###public の特徴
・webpackでコンパイルされない
・CSSのbackground-imageで利用する場合は必ず画像をpublicに入れる必要がある

###参考サイト
https://webrandum.net/vue-cli-image-path/

#おわりに
jsonに画像パスを書いてaxiosで呼び出すことなどあまりないと思いますが、フロントだけの実装練習の時に使用しました。
以前もbackground-imageで画像を参照できずにpublicを使用しましたが、jsonでもpublicを使用する必要があったようです。2つのディレクトリの違いをもっと早く調べていればよかった。

解決に時間がかかった理由のひとつに、検索してもassetsstaticの比較ばかり出てくるのですが、このstaticフォルダが何のことだか分からなかった事があります。VueCLIバージョン2でstaticだったものが、バージョン3で名前が変わりpublicになったようです。