Nuxt.jsでもVue.jsで同様にaxiosを利用することで外部からデータを取得することができます。外部からのデータ取得はアプリケーションを構築する上で表示に重要な部分なので本文書ではNuxt.jsにおけるaxiosの使い方とasyncDataの使い方について説明を行っています。

Nuxt.jsではサーバサイドでデータを取得する際はasyncData Hookまたはfetch Hookの中にデータを取得する処理を記述します。クライアントサイドでデータを取得する際にはmounted Hookの中にデータを取得する処理を記述します。

fetch HookはJavaScriptのfetch関数とは異なります。また本文書ではfetch Hookについての説明は行っておりません。
fukidashi

本文書の内容はルーティングやtitleタグの設定などNuxt.jsの基礎知識が必要となります。Nuxt.jsがわからない場合はまず下記の文書を読むことをおすすめします。

外部のサービスについて

axiosを使って外部からデータを取得するためにはサーバを準備する必要があります。今回はデータの取得のみに重点を行なっているため動作確認に利用できる無料のJSONPlaceholderを利用します。Vue.jsにおけるaxiosの基本的な操作については下記の文書を参考にしてみてください。

Axiosの利用方法

Nuxt.jsをインストールする際にモジュールの選択項目がありますがそこでaxiosを選択している場合はaxiosの追加のインストールは必要ありません。しかし今回はaxiosに注目しているためaxiosモジュールをインストールしていない状態から動作確認を行っていきます。

Nuxt.jsではaxiosはライブラリのaxiosとnuxt.js用の機能が追加されているモジュールの@nuxtjs/axiosを利用することができます。どちらも同じaxiosですが記述方法が異なります。

@nuxtjs/axiosはデフォルトでプログレスバーの表示を行うことができnuxt.config.jsファイルを使ってオプション設定を行うこともできます。

axiosの使用方法を確認した後に@nuxtjs/axiosの使用方法を確認していきます。

nuxt.jsのインストール時に選択してインストールされるaxiosは@nuxtjs/axiosです
fukidashi

axiosのインストール

npmコマンドを利用してaxiosのインストールを行います。


 $ npm install axios

記事データの取得と一覧表示

インストールが完了したらaxiosを使ってJSONPlaceholderからpostsデータの取得を行います。postsデータを取得するために以下のURLへアクセスします。


https://jsonplaceholder.typicode.com/posts
ブラウザでhttps://jsonplaceholder.typicode.com/postsにアクセスするとどのような情報が取得できるか確認することができます。
fukidashi

取得したpostsデータを表示させるページの作成を行います。pagesディレクトリの中にpostsディレクトリを作成して、その下にindex.vueファイルを作成します。

Nuxt.jsではpagesディレクトリの下にディレクトリやファイルを作成すると自動でルーティングの設定を行ってくれます。pages/postsの下にindex.vueを作成すると/posts/にアクセスがあればindex.vueファイルの内容が表示されます。
fukidashi

axiosを利用するためにはaxiosをimportする必要があります。axiosで取得したデータをv-forを使って展開します。


<template>
<div>
  <h1>記事一覧</h1>
  <ul>
    <li v-for="post in posts" :key=post.id>
        {{ post.title }}
    </li>
  </ul>
</div>
</template>

<script>
import axios from 'axios';

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

ブラウザで確認すると記事一覧が表示されます。

axiosで記事一覧取得
axiosで記事一覧取得

@nuxt/axiosをmounted hookで利用できないわけではなくaxiosをimportすることなくthis.$axiosで利用することができます。


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

記事の個別ページの取得

次は記事の個別ページの情報を取得します。アクセスするURLは下記の通りです。

postsの/(スラッシュ)の後にある数字は下記では1になっていますが、1だけではなく2, 3,…と記事の数だけ異なるURLが存在します。


https://jsonplaceholder.typicode.com/posts/1

記事の数だけ個別ページを作成するのではなく1つのページを作成しそのページを共有して利用します。URLに含まれる記事IDによってサーバから取得する記事データが異なるのでURLに含まれる数字または文字列を取得する必要があります。

postsディレクトリの下に_id.vueファイルを作成します。_id.vueファイルという名前をつけた場合はpostsの後ろの数字はthis.$route.params.idで取得することができます。

idではなく_postId.vueとした場合はthis.$route.parames.postIdでURLに含まれる数字または文字を取得することができます。名前は任意の名前をつけることができます。
fukidashi

Nuxt.jsではheadプロパティを使うことで各ページにtitleやmetaタグの情報を追加することができます。ここではheadプロパティを利用してtitleを設定します。titleはページ毎に異なるため取得したpostデータのtitleを利用します。


<template>
<div>
  <h1>記事個別ページ{{ post.id }}</h1>
  <h2>{{ post.title }}</h2>
  <p>{{ post.body}}</p>
</div>
</template>

<script>
import axios from 'axios';

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

ブラウザで確認すると個別ページの情報が表示されます。

記事の個別ページ
記事の個別ページ

ブラウザの表示では問題はありませんが、ブラウザからページのソースを確認するとtitleタグにはデフォルトの値が入っており_id.vueで設定したpost.titleは入っていません。

titleのデフォルトの値はnuxt.config.jsファイルのheadプロパティに記述されています。
fukidashi

この原因はNuxt.jsのmodeがデフォルトではuniversalに設定されておりSever Side Rendering(SSR)機能によりサーバ側でheadが設定されるためです。そのためライフサイクルフックのmountedの処理(ブラウザ側で行われる処理)でpostデータが取得できてもheadを設定することができません。この問題を解決するためにサーバ側でpostデータを取得する処理を行う仕組みが必要になります。

Nuxt.jsではサーバ側で処理を行うためにasyncDataが準備されています。

まずasyncDataの使用方法を確認しその後にheadへの値の反映について説明を行っていきます。

asyncDataの使用方法

サーバ側で実行される処理asyncDataの使い方を確認していきます。

asyncDataはサーバ側で行う処理なのでコンポーネントのdataプロパティの設定を行うことができます。asyncDataはページのコンポーネントがロードされる前に呼ばれ、asyncDataを使うと下記のようにtitleを設定することが可能です。


export default {
  asyncData(){
    return{
      title: '個別ページ'
    }
  },
  head(){
    return {
      title: this.title
    }
  },
  data(){
    return {
      post: {},
    }
  },
  mounted(){
    axios.get('https://jsonplaceholder.typicode.com/posts/' + this.$route.params.id)
          .then(response => this.post = response.data);
  }
}

asyncDataはサーバ側で実行されるので、ブラウザからページのソースを確認するとtitleに個別ページが入っていることが確認できます。

コンポーネントのdataプロパティと同様に扱えるのでtemplateタグの中に{{ title }}を追加すると”個別ページ”と表示されます。
fukidashi

asyncDataを利用してpostsデータ取得

asyncDataを利用してサーバ側でpostsデータの取得を行います。pages/postsディレクトリのindex.vueファイルでasyncDataを利用して記事一覧を取得します。

asyncDataを利用する前はpostsデータを取得し、表示させるためにはdataプロパティとmountedが必要でしたがどちらも削除することができます。取得したデータpostsはdataプロパティなのでそのままtemplateタグで使用することができます。


import axios from 'axios';

export default {
  asyncData() {
    return axios.get('https://jsonplaceholder.typicode.com/posts/')
                .then(response => {
                  return {
                    posts: response.data
                  }
                })
  },
  // data(){
  //   return {
  //     posts: []
  //   }
  // },
  // mounted(){
  //   axios.get('https://jsonplaceholder.typicode.com/posts')
  //         .then(response => this.posts = response.data);
  // }

async, awaitを利用してコードを書き換え

asyncとawaitを利用して先程のコードを書き換えることができます。実行してもブラウザ上の結果は変わりません。


async asyncData() {
  const response = await axios.get('https://jsonplaceholder.typicode.com/posts/')
  return { posts: response.data }
},

複数の場所からaxiosを使ってデータを取得する場合はasyncとawaitを利用することで下記のように記述することができます。


async asyncData() {
  const responsePost = await axios.get('https://jsonplaceholder.typicode.com/posts/')
  const responseUser = await axios.get('https://jsonplaceholder.typicode.com/users/')
  return { posts: responsePost.data, users:responseUser.data }
},

asyncDataを利用して個別データを取得

個別データの場合はURLに入った記事のIDが必要になります。mountedを利用していた場合はthis.$route.params.idで取得することができましたがasyncDataではこの値を利用することはできません。asyncDataではcontextを利用することでidを取得することができます。


async asyncData(context) {
  const response = await axios.get('https://jsonplaceholder.typicode.com/posts/' + context.params.id)
  return { post: response.data }
},

contextはconsole.log(context)で確認することも可能です。contextにはさまざまな値が入っていますがES6のDestructuringを利用して記述を短縮化することができます。


async asyncData( { params } ) {
  const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts/' + params.id)
  return { post: data }
},

記事データはサーバ側で取得しているので、mountedでデータを取得した時に設定できなかったheadのtitleも設定されていることも確認できます。

ソースからtitleの確認
ソースからtitleの確認

nuxtjs/axiosモジュールのインストール

次は@nuxtjs/axiosモジュールをインストールして動作確認を行います。

Nuxt.jsのインストール中にaxiosを選択すると@nuxtjs/axiosモジュールがインストールされます。

インストールはnpmコマンドを利用します。


$ npm install @nuxtjs/axios

インストール後nuxt.config.jsファイルにaxiosの情報を登録する必要があります。


  modules: [
    '@nuxtjs/axios',  
  ],

nuxtjs/axiosモジュールを利用したデータ取得

axios単体ではファイル毎にimportでaxiosを読み込んでいましたが、nuxtjs/axiosの場合はcontextを利用してaxiosを使います。

axiosの記述方法ですがaxios.getからcontext.$axios.$getに変更になります。context.$axios.$getに変更するとresponseではなく直接response.dataが戻されます。


async asyncData(context) {
  const posts = await context.$axios.$get('https://jsonplaceholder.typicode.com/posts/')
  return { posts }
},
$getではなくgetにするとresponseが戻されます。{ posts } は{ posts : posts }を短縮したものです。
fukidashi

contextをES6のDestructuringを利用して記述を短縮化することができます。


async asyncData({$axios}) {
  const posts = await $axios.$get('https://jsonplaceholder.typicode.com/posts/')
  return { posts }
},

nuxtjs/axiosを利用したプログレスバーの設定

nuxtjs/axiosではデフォルト設定のままプログレスバーを表示させることができます。

プログレスバーはページを移動する際に移動先のページが表示されるまで上部に表示されます。デフォルトでも設定されているのですが、colorが#fff(白)のため背景に隠れて存在を確認することができません。nuxt.config.jsファイルで色の設定を変更します。


  ** Customize the progress-bar color
  */
  loading: { color: '#FF8733' },
  /*

index.vueで取得した各記事にnext-linkタグをつけてリンクを貼ります。


<template>
<div>
  <h1>記事一覧</h1>
  <ul>
    <li v-for="post in posts" :key=post.id>
        <nuxt-link :to="'/posts/' + post.id">{{ post.title }}</nuxt-link>
    </li>
  </ul>
</div>
</template>

記事のリンクボタンをクリックすると個別ページが表示される前に上部にプログレスバーが表示されることを確認できます。

nuxtjs/axiosのオプションの設定

nuxtjs/axiosでオプションの設定をnuxt.config.jsファイルで行うことができます。Nuxt.jsを使ってインストールをした場合に下記は自動でnuxt.config.jsファイルに存在しますが、Nuxt.jsのインストールとは別にnuxtjs/axiosをインストールした場合は追加する必要があります。


  modules: [
    '@nuxtjs/axios',  
  ],
  /*
  ** Axios module configuration
  ** See https://axios.nuxtjs.org/options
  */
  axios: {
  },

一番簡単なbaseURLを設定して動作確認を行ってみましょう。

baseURLでURLを登録しておくとこれまでhttps://jsonplaceholder.typicode.com/posts/と記述していましたが、/posts/のみの記述で問題ありません。


  axios: {
    baseURL: 'https://jsonplaceholder.typicode.com/'
  },

nuxt.config.jsを更新したのでindex.vueのURLを変更することができます。


async asyncData(context) {
  const posts = await context.$axios.$get('/posts/')
  return { posts }
},

変更しても表示される内容は変化はありません。

modeをspaした時のasyncDataの動作

nuxt.config.jsファイルのmodeをspaに変更することでサーバサイドレンダリングを停止することができます。設定に変更してもasyncDataでデータの取得を行われます。設定が反映されていないのではと思うかもしれませんがブラウザのソースを確認することでサーバから戻されるデータに取得したデータ含まれていないことを確認することができます。データ取得はサーバサイドで行われるのではなくクライアントサイドで行われデータも表示されます。headのtitleの値などはサーバ側で更新されないためasyncDataで設定している場合はデフォルトの値が表示されることになります。