Vuetify 3のv-imgで@を使って画像を読み込めない問題の解決方法

これはなに Link to this heading

Vuetify 2で動いていたv-imgのコードがVuetify 3で動かなくなったので対処した。 そのときの対処法を記す。

環境 Link to this heading

  • node v19.3.0
  • Vue 3.2.45
  • Vite 3.2.5
  • Vuetify 3.0.5
  • typescript 4.9.4

背景 Link to this heading

Vuetifyを2から3にアップグレードした。 そしたら、いままで読み込めていた画像が読み込めなくなった。 どうやらpath aliasで定義されていた@がきちんと処理されていない模様。

Vuetify 2だと、下記の記述で問題なく画像が読み込まれていた。

<v-img src="@/assets/logo.png" />

この記述をVuetify 3で使うと、下記のエラーが出て、画像は読み込めない。

Failed to load resource: the server responded with a status of 404 (Not Found)

ちなみに、Vuetifyのv-imgタグではなく、普通のimgタグだと問題なく読み込める。 すなわち、下記の書き方なら、きちんと画像が表示される。

<img src="@/assets/logo.png" />

普通のimgタグなら読み込めるため、path aliasは問題なく設定できていると思われる。つまりv-imgタグが@をうまく処理できていないとみられる。

何が原因だったのか? Link to this heading

Vuetify 3が動くVue 3では、ビルドツールがvue-cliからViteに移行した。 どうやらその関係で動かなくなったっぽい。

対処法 Link to this heading

動作が確認できている対処法は次の2つ。 どちらかの対応を取ればよい。

  1. 画像ファイルをimportする
  2. 画像ファイルをpublic配下に配置する

どちらの方法が良いのか? Link to this heading

画像ファイルをimportする方法では、画像ファイルも含めてビルドされる。 一方で画像ファイルをpublic配下に配置する方法では、画像ファイルはビルドに含まれない。 よって、コンポーネントの一部として用いられる画像はimportし、それ以外の画像はpublic配下に置くのが良さそう。

1. 画像ファイルをimportする方法 Link to this heading

<script>ブロック内で画像ファイルをimportし、それを利用する方法。

まず、<script>ブロックで、適当な変数名で画像ファイルをimportする。 このときはpath aliasである@/が使える。

<script setup lang="ts">
  import logo_img from '@/assets/logo.svg';
</script>

次に、<template>ブロック内に画像を配置する。 src属性は変数になるのでbindさせる必要がある。 ゆえに、srcではなく:src(あるいはv-bind:src)にしなければならない。

<template>
  <!-- ... -->
  <v-img contain height="300" :src="logo_img" />
  <!-- ... -->
</template>

これで表示自体はできるようになる。 yarn devでローカルサーバを立ち上げると、きちんと画像が表示される。 しかし、このままだとTypeScriptで下記のエラーを吐かれる。

モジュール '@/assets/logo.svg' またはそれに対応する型宣言が見つかりません。ts(2307)

ビルド時にも、下記のエラーが発生してビルドが通らない。

error TS2307: Cannot find module '@/assets/logo.svg' or its corresponding type declarations.

これはTypeScriptの仕様である。TypeScriptでコード以外のアセットを利用するためには、そのインポートに対して型を繰り下げる必要がある。どうすればよいかというと、公式 にもあるように、.svgファイルのcustom definitionsを用意すればよい。

まず、custom.d.tsファイルを作り、svgの拡張子の型を定義する。

custom.d.ts
declare module '*.svg' {
  const content: any;
  export default content;
}

次に、定義した型をTypeScriptのコンパイラに伝えるため、tsconfig.jsonにcustom.d.tsファイルの存在を追記する。

tsconfig.json
{
  "include": [
    "src/**/*.ts",
    "custom.d.ts"
  ]
}

こうすると、TypeScriptのエラーは解消され、yarn buildも通るようになる。

2. 画像ファイルをpublic配下に配置する方法 Link to this heading

これはもっと簡単にできる。

まず、画像ファイルをpublicディレクトリ下に配置する。 たとえば、public/assetsディレクトリ内にlogo.pngを置く。 そして、それをv-imgsrcで参照する。

次に、<template>ブロック内で画像ファイルを参照する際、src属性に/assets/logo.pngを指定する。例を次に示す。

<template>
  <!-- ... -->
  <v-img contain height="300" src="/assets/logo_test.svg" />
  <!-- ... -->
</template>

これで、画像ファイルが正常に表示される。

まとめ Link to this heading

Vuetify 3でv-imgが動かなくなった場合、以下の2つの方法で対処できる。

  1. 画像ファイルをimportして使用する
  2. 画像ファイルをpublicディレクトリ下に配置して使用する

どちらの方法でも、画像の表示は問題なくできる。コンポーネントに密接に関連する画像などはimportし、それ以外の画像はpublicディレクトリ下に置くという使い分けが適切だろう。

参考文献・URL Link to this heading

Hugo で構築されています。
テーマ StackJimmy によって設計されています。