大ちゃんの駆け出し技術ブログ

RUNTEQ受講生のかわいいといわれるアウトプットブログ

【Nuxt】NuxtでAxios

はじめに

こんにちは!大ちゃんの駆け出し技術ブログです。

今回はNuxt.jsでAxiosを使用した簡単なチュートリアルを記述します。

API取得サイト

AxiosはJSONレスポンスを受け取るのでJSONレスポンスを返却するサイトが必要です。

今回はAPIサーバを自分で設けず、JSONのレスポンスを返してくれる以下のサイトを利用します。

JSONPlaceholder

試しにブラウザ上で以下のURLにアクセスしてみましょう。

https://jsonplaceholder.typicode.com/users

すると以下のようにJSONレスポンスを返してくれます。

https://i.gyazo.com/3bfe38ff51825dfb8a59d38b6bb239d0.png

導入

まずはプロジェクトを立ち上げます。プロジェクトの設定ですが、下記記事と同じ設定にしておりますのでご参照ください。

【Nuxt】プロジェクト立ち上げ - 大ちゃんの駆け出し技術ブログ

プロジェクトを作成したら下記コマンドでaxiosをインストールします。

$ yarn add axios

ユーザ一覧ページ

ユーザ一覧ページを実装します。せっかくvuetifyを使用していうのでv-listタグを使用してユーザ一覧を表示します。

まずusersディレクトリをpages配下に作成します。その後usersディレクトリ配下にlist.vueを作成し、以下のように記述しましょう。

<!-- pages/users/list.vue -->
<template>
  <v-list three-line>
    <template v-for="user in users">
      <v-list-item :key="user.id" nuxt>
        <v-list-item-content>
          <v-list-item-title v-html="user.name"></v-list-item-title>
          <v-list-item-subtitle v-html="user.email"></v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </template>
  </v-list>
</template>

<script>
import axios from "axios";

export default {
  data() {
    return {
      users: []
    };
  },
  mounted() {
    axios
      .get("https://jsonplaceholder.typicode.com/users")
      .then(response => (this.users = response.data));
  }
};
</script>

mounted時にユーザ一覧を返すJSONをGETでリクエストし、レスポンスをusersに格納しています。

mounted() {
  axios
    .get("https://jsonplaceholder.typicode.com/users")
    .then(response => (this.users = response.data));
}

http://localhost:3000/users/listにアクセスすると以下のようなページが表示されるはずです。

https://i.gyazo.com/6793f9c6e7c12b3800b089c16ddb28d4.png

ユーザ詳細ページ

続いてはユーザ詳細ページを表示させます。これは前回の記事と同じような内容です。

詳細ページを作成するためにファイル名は「_ + プロパティ名」でした。今回のユーザデータの場合、_id.vue_name.vueなどが作成可能です。

それでは以下のようなファイル作成してみましょう。

<!-- pages/users/_id.vue -->
<template>
<div>
  <h1>ユーザ{{ user.id }}</h1>
  <h2>{{ user.name }}</h2>
  <p>{{ user.email}}</p>
</div>
</template>

<script>
import axios from 'axios';

export default {
  head(){
    return {
      title: this.user.name
    }
  },
  data(){
    return {
      user: {},
    }
  },
  mounted(){
    axios.get('https://jsonplaceholder.typicode.com/users/' + this.$route.params.id)
          .then(response => this.user = response.data);
  }
}
</script>

mounted時にvue-routerのパラメータと一致するユーザ情報を受け取っています。

mounted(){
  axios.get('https://jsonplaceholder.typicode.com/users/' + this.$route.params.id)
        .then(response => this.user = response.data);
}

http://localhost:3000/users/1にアクセスすると以下のようなページが表示されるはずです。

https://i.gyazo.com/7387ba39dc8d28c264c0dd213ee85623.png

ユーザ一覧からユーザ詳細ページにアクセスできるようにしましょう。先ほどのユーザ一覧ページのファイルのtemplateタグ内を以下のようにします。

<template>
  <v-list three-line>
    <template v-for="user in users">
      <v-list-item
        :key="user.id"
        nuxt <= 追加
        :to="{ name: 'users-id', params: { id: user.id } }" <= 追加
        >>
        <v-list-item-content>
          <v-list-item-title v-html="user.name"></v-list-item-title>
          <v-list-item-subtitle v-html="user.email"></v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </template>
  </v-list>
</template>

v-list-itemnuxtプロパティを追加することでリンクがnuxt-linkであることを指定します。 :to="{ name: 'users-id', params: { id: user.id } }"は前回の記事と同じ実装方法です。

これでユーザ一覧ページから詳細ページに遷移できます。

asyncData

現状のユーザ一覧ページと詳細ページはどちらも先にページが読み込まれ、その後axiosで取得したデータを表示する実装になっています。例えば、ユーザ一覧ページの場合、先にヘッダーやナビゲーションバーなどが読み込まれて、その後にユーザ一覧が表示されます。

https://i.gyazo.com/0013742f7e1b265f96a675e21fb91615.gif

しかし、二度表示される実装はあまりきれいではありません。ユーザ一覧も同時に表示させたいところ。

そこで使用するのがasyncDataプロパティです。

データの取得

asyncDataはNuxtのライフサイクルの一つです。レンダリングされる前に外部APIからのデータを取得することができるようです。

https://i.gyazo.com/b0caac114f7829cc822fb269c410b11b.png

Vue.jsではこのライフサイクルがないため、mounted時にAPIデータを取得するしかなく、ページ表示とずれたタイミングで取得データを表示してしまいます。しかし、asyncDataを使用すればページを表示する前にデータを取得することができるので一度の表示で完結します。

ちなみに下記のように記載するようです。見た目がdataプロパティに似ていますね。

asyncData() {
  return axios
    .httpリクエスト(url)
    .then(response => {
      return {
        データ名: response.data
      };
    });
}

asyncDataでユーザ一覧ページ

百聞は一見にしかず。まずはユーザ一覧ページを以下のように変更します。

<template>
  <v-list three-line>
    <template v-for="user in users">
      <v-list-item
        :key="user.id"
        nuxt
        :to="{ name: 'users-id', params: { id: user.id } }"
        >>
        <v-list-item-content>
          <v-list-item-title v-html="user.name"></v-list-item-title>
          <v-list-item-subtitle v-html="user.email"></v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </template>
  </v-list>
</template>

<script>
import axios from "axios";

export default {
  asyncData() {
    return axios
      .get("https://jsonplaceholder.typicode.com/users/")
      .then(response => {
        return {
          users: response.data
        };
      });
  }
};
</script>

これによりデータを取得した後にページが表示されるようになりました。

https://i.gyazo.com/41160f2ac28eec699ab22d0158784123.gif

context

ユーザ一覧ページと同じように既にあったmoutedを置換する形で実装することはできません。asyncDataのライフサイクル時にthisにアクセスすることができないからです。

asyncData は pages でのみ使用可能で、このフック内では this にアクセスすることはできません。

データの取得

ではどうするのかというとコンテキストを使用します。

context は、Nuxt から Vue コンポーネントに追加のオブジェクト/パラメータを提供し、asyncData、fetch、plugins、middleware、nuxtServerInit のような特別な Nuxt ライフサイクル内で使用できます。

コンテキスト

contextの中身を確認するためにconsole.logで出力してみましょう。

<!-- pages/users/_id.vue -->
<template>
  <div></div>
</template>

<script>
import axios from "axios";

export default {
  async asyncData(context) {
    console.log(context);
  }
};
</script>

するとコンソールに膨大なデータが格納されていることがわかります。

https://i.gyazo.com/b0ab031756b266fe77cddcb93ff87642.gif

これを使用して詳細ページを表示させます。

asyncDataでユーザ詳細ページ

以下のように書き換えましょう。

<!-- pages/users/_id.vue -->
<template>
  <div>
    <h1>ユーザ{{ user.id }}</h1>
    <h2>{{ user.name }}</h2>
    <p>{{ user.email }}</p>
  </div>
</template>

<script>
import axios from "axios";

export default {
  async asyncData(context) {
    const response = await axios.get(
      "https://jsonplaceholder.typicode.com/users/" + context.params.id
    );
    return { user: response.data };
  }
};
</script>

こちらも一度にページが表示されるようになりました。

https://i.gyazo.com/73c4fcfd73b48fd4c1e4b438603c5c67.gif