【Vue.js】JSONファイルからデータを取得する方法

【Vue.js】JSONファイルからデータを取得する方法 Vue.js

設定値や商品リストのような、中身がほとんど変わらないデータは、JSONファイルにしてプロジェクトに入れておくと扱いやすくなります。この記事では、Vue 3(<script setup>)で、JSONファイルを取得して表示する方法を、置き場所の選び方とエラー処理を含めて整理します。

スポンサーリンク

JSONファイルの準備

まず、表示するデータをJSONで用意します。例として、data.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に入れます。読み込み中とエラーの状態も、あわせて持たせるのが基本です。

Vue:fetchでJSONを取得する
<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:baseと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 }のような名前付きインポートも使え、使わない部分をビルドで削れます。

Vue:JSONをimportして使う
<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も使えます。最初にインストールします。

Shell:axiosのインストール
npm install axios
Vue:axiosでJSONを取得する
<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の状態管理パターンで解説しています。

関連記事

よくある質問(FAQ)

Q存在しないJSONを指定したのに、分かりにくいエラーが出るのはなぜですか?
Afetch()は404でもrejectしないため、返ってきたHTMLをJSONとして読もうとして失敗するためです。response.okを確認し、falseならthrowしてください。Viteの開発サーバーでは、存在しないパスでもindex.htmlが200で返るため、content-typeがJSONかどうかも確認すると確実です。
QJSONはpublicとsrcのどちらに置くべきですか?
A配備後にJSONだけを差し替えたいならpublicに置いてfetchします。内容が固定ならsrcに置いてimportすると、通信が不要で、すぐに使えます。
Qfetchとaxiosのどちらを使うべきですか?
A単純なJSONの取得なら、追加のインストールが不要なfetchで十分です。4xx・5xxの自動エラー処理や、共通の設定をまとめたい場合はaxiosが便利です。
Qサブディレクトリに配備したら、JSONが取得できなくなりました。
A/data.jsonのような絶対パスは、ドメイン直下を指すためです。Viteのbaseを設定し、import.meta.env.BASE_URLを使ってパスを組み立ててください。

まとめ

JSONファイルは、差し替えたいならpublicに置いてfetch、固定ならsrcからimport、と使い分けます。fetchはHTTPエラーでrejectしないので、response.okの確認を忘れずに、読み込み中とエラーの状態も画面に出しておくと、原因を追いやすくなります。