設定値や商品リストのような、中身がほとんど変わらないデータは、JSONファイルにしてプロジェクトに入れておくと扱いやすくなります。この記事では、Vue 3(<script setup>)で、JSONファイルを取得して表示する方法を、置き場所の選び方とエラー処理を含めて整理します。
JSONファイルの準備
まず、表示するデータをJSONで用意します。例として、data.jsonという名前で次の内容を作ります。
{
"items": [
{ "id": 1, "name": "Item 1", "description": "This is item 1" },
{ "id": 2, "name": "Item 2", "description": "This is item 2" },
{ "id": 3, "name": "Item 3", "description": "This is item 3" }
]
}
置き場所は2通り:publicとsrc
ViteでVueのプロジェクトを作っている場合、JSONの置き場所と、読み込み方は2通りあります。
my-app/
├─ public/
│ └─ data.json ← ここに置くと /data.json で取得できる
└─ src/
├─ data/
│ └─ items.json ← import で取り込む場合はここ
└─ App.vue
- publicに置く:ファイルはそのまま配信され、ルートの
/data.jsonのような絶対パスでfetchできます。ビルドしたあとも、JSONだけを差し替えられるのが利点です。importはしません。 - srcに置いてimportする:ビルド時にバンドルへ取り込まれるので、通信が発生せず、すぐに使えます。内容を変えるには、再ビルドが必要です。
後から内容を差し替えたいデータはpublic、固定の定数のようなデータはsrcからのインポートが向いています。
fetchで取得して表示する
onMountedの中でfetchを呼び、結果をitemsに入れます。読み込み中とエラーの状態も、あわせて持たせるのが基本です。
<script setup>
import { ref, onMounted } from 'vue'
const items = ref([])
const loading = ref(true)
const error = ref(null)
onMounted(async () => {
try {
const response = await fetch('/data.json')
// fetch は 404 や 500 でも reject しない。自分で確認して throw する
if (!response.ok) {
throw new Error(`取得に失敗しました(ステータス ${response.status})`)
}
// 開発サーバーは、存在しないパスにも index.html を 200 で返すことがある。
// JSON ではなく HTML が返ってきていないかも確認する
const type = response.headers.get('content-type') || ''
if (!type.includes('application/json')) {
throw new Error('JSON ではないデータが返ってきました。パスを確認してください')
}
const data = await response.json()
items.value = data.items
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
})
</script>
<template>
<p v-if="loading">読み込み中…</p>
<p v-else-if="error">エラー:{{ error }}</p>
<ul v-else>
<li v-for="item in items" :key="item.id">
<h2>{{ item.name }}</h2>
<p>{{ item.description }}</p>
</li>
</ul>
</template>
fetchは404でもrejectしない
fetch()が失敗(reject)するのは、ネットワークエラーのような場合だけです。サーバーが404や500を返しても、fetch()は成功として扱います。このため、response.okを確認しないと、存在しないJSONを指定したときに、エラーページのHTMLをJSONとして解釈しようとして、分かりにくい構文エラーが出ます。上のコードのように、response.okがfalseなら、自分でthrowしてください。
もう1つ注意があります。Viteの開発サーバーは、既定(appTypeがspa)で、一致しないパスにindex.htmlを返すSPAフォールバックを使います。publicにJSONを置き忘れたり、パスを間違えたりしても、開発中はHTMLが200で返ることがあり、response.okだけでは気づけません。上のコードでは、content-typeがJSONかどうかも確認しています。
サブディレクトリに配備するとき
/data.jsonのような絶対パスは、サイトがドメイン直下にあることが前提です。サブディレクトリに配備するときは、Viteのbaseを設定し、import.meta.env.BASE_URLでパスを組み立てます。この変数はビルド時に置き換えられるため、import.meta.env['BASE_URL']のような書き方はできません。
// vite.config.js:サブディレクトリに配備する場合
export default {
base: '/my/public/path/'
}
// コード側:BASE_URL を使ってパスを組み立てる
const response = await fetch(`${import.meta.env.BASE_URL}data.json`)
なお、HTMLファイルを直接ブラウザで開く(file://)と、fetchは動きません。開発サーバー(npm run dev)か、配備先のサーバー経由で確認してください。
srcからimportして使う
ViteはJSONファイルを、そのままimportできます。通信がないので、読み込み中やエラーの状態も不要です。{ items }のような名前付きインポートも使え、使わない部分をビルドで削れます。
<script setup>
// src/data/items.json を、ビルド時にバンドルへ取り込む(通信は発生しない)
import data from './data/items.json'
// 一部のフィールドだけなら、名前付きインポートも使える(不要な部分を削れる)
// import { items } from './data/items.json'
const items = data.items
</script>
<template>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</template>
axiosを使う場合
fetchの代わりに、HTTPクライアントのaxiosも使えます。最初にインストールします。
npm install axios
<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
const items = ref([])
const error = ref(null)
onMounted(async () => {
try {
// axios は 4xx / 5xx で自動的に reject する(response.ok の確認は不要)
const response = await axios.get('/data.json')
items.value = response.data.items // JSON は response.data に入る
} catch (e) {
error.value = e.message
}
})
</script>
axiosは、4xxや5xxのステータスで、自動的にエラー(reject)になります。fetchのようにresponse.okを確認する必要はありません。JSONの中身はresponse.dataに入ります。APIからの取得や、通信状態の統一的な扱いは非同期データの取得と表示、非同期APIの状態管理パターンで解説しています。
関連記事
- 取得したデータの表示はv-forで配列やオブジェクトをループして表示する方法
- 読み込み中・エラーの出し分けはv-ifで条件分岐させる方法
- 環境ごとの接続先の切り替えは環境ごとにAPIのURLを切り替える方法
- 値が変わったときに再取得するならwatchを活用してデータの変化を監視する方法
よくある質問(FAQ)
fetch()は404でもrejectしないため、返ってきたHTMLをJSONとして読もうとして失敗するためです。response.okを確認し、falseならthrowしてください。Viteの開発サーバーでは、存在しないパスでもindex.htmlが200で返るため、content-typeがJSONかどうかも確認すると確実です。publicに置いてfetchします。内容が固定ならsrcに置いてimportすると、通信が不要で、すぐに使えます。fetchで十分です。4xx・5xxの自動エラー処理や、共通の設定をまとめたい場合はaxiosが便利です。/data.jsonのような絶対パスは、ドメイン直下を指すためです。Viteのbaseを設定し、import.meta.env.BASE_URLを使ってパスを組み立ててください。まとめ
JSONファイルは、差し替えたいならpublicに置いてfetch、固定ならsrcからimport、と使い分けます。fetchはHTTPエラーでrejectしないので、response.okの確認を忘れずに、読み込み中とエラーの状態も画面に出しておくと、原因を追いやすくなります。
