axios通信時にNuxt.jsのローディングアニメーションを実装する

はじめに

こんにちは!SaaS事業部エンジニアの小川です。

普段はSaaS事業部でKUROTEN.という経営管理SaaSの開発をしています。

今回は、Nuxt.jsでaxios通信時にローディングアニメーションを表示する方法を紹介します。

NuxtのLoading設定(Nuxt標準)

基本的にNuxt.jsでローディングアニメーションを実装する場合、nuxt.config.jsのloadingオプションをtrueにする方法がよく使われていると思います。 ここでいうローディングは、ページ遷移の間の読み込み時に動作します。

# nuxt.config.js
export default {
    loading: true
}

また、こちらのローディングオプションにはオブジェクトを渡してあげることで、ある程度細かく見た目を変えることもできます。(以下では一例を紹介)

# nuxt.config.js
export default {
    loading: {
        progress-bar-height: 2px, // プログレスバーの高さ
        rtl: false // プログレスバーの向きを指定
    }
}

さらに見た目をカスタマイズしたい場合は以下のようにコンポーネントを用意し、 NuxtのLoadingオプションにコンポーネントを設定することも可能です。

// nuxt.config.js
export default {
    Loading: './commponents/Loading'
}

axios通信時にLoadingを動かす

ここからが本題です。 Nuxt.jsでaxios通信時にローディング処理を行いたい場合、以下の2つの手法があります。

this.loading.startとthis.loading.finishで通信中を取得

公式で紹介されている方法で、 axios通信している箇所にstartfinishの処理を追加するだけで実装できるのでお手軽です。 あとは先ほど紹介したNuxtのLoadingオプションをtrueにすれば従来どおりの方法でローディングが動作します。

// ローディング処理を入れたい箇所
this.loading.start ()
hoge () // axiosの処理
this.loading.finish ()

storeとpluginのaxiosでローディングの状態を管理する

上で述べた手法でもaxios通信時にローディング処理を走らせることができますが、 呼び出し処理を追加するたびにstartとfinishの記述が必要になってしまうため 通信処理が複数あると開発者の負担が大きいように思います。

そこで、storeでローディングの状態管理をすることで、axios通信時の処理を一括して記述する方法をご紹介します。

まずはloadingの状態を管理するためのstoreを追加します。

// ./store/loading.js

export const state = () => ({
  loadingCount: 0
})

export const getters = {
  loadingCount: (state) => state.loadingCount,
  isLoading: (state) => state.loadingCount > 0
}

export const mutations = {
  incrementLoadingCount (state) {
    ++state.loadingCount
  },
  decrementLoadingCount (state) {
    if (state.loadingCount > 0) {
      --state.loadingCount
    }
  }
}

export const actions = {
  incrementLoadingCount ({ commit }) {
    return commit('incrementLoadingCount')
  },
  decrementLoadingCount ({ commit }) {
    return commit('decrementLoadingCount')
  }
}

次にaxiosを用いた通信中にLoadingCountが1以上になるようpluginsのaxios.jsに先ほど作成したアクションを追加します。 このpluginsはaxiosのリクエストやレスポンスをフックにして呼び出されるので、共通化したい場合にとても便利です。

// ./plugins/axios.js

export default function({ $axios, store }) {
    $axios.onRequest(config => {
        store.dispatch('loading/incrementLoadingCount')
    })

    $axios.onResponse(() => {
        store.dispatch('loading/decrementLoadingCount')
    })
    
    $axios.onError(e => {
        store.dispatch('loading/decrementLoadingCount')
    })
}

あとは上述で作成したLoadingCountをトリガーにして読み込みのオーバーレイやアニメーションを表示すれば完成です。 以下は通信中にVuetifyのプログレスバーを動かすサンプルです。LoadingCountをトリガーに使うだけなので、自由度は非常に高いです。

// ./components/Loading.vue
<template>
  <v-progress-linear
    :active="isLoading"
    :indeterminate="isLoading"
    :height="loadingHeight"
    absolute
    bottom
    :color="loadingColor" />
</template>

<script>
  import { mapGetters } from 'vuex'

  export default {
    name: 'Loading',
    data () {
      return {
        loadingHeight: '5',
        loadingColor: '#4DB6AC'
      }
    },
    computed: {
      ...mapGetters({
        isLoading: 'loading/isLoading'
      })
    }
  }
</script>

先程実装したLoadingコンポーネントをHeaderコンポーネントで呼び出し、layoutsのdefaultへ渡すことで、全ページにローディング処理の実装ができます。

// ./components/Header.vue

<template>
  <v-app-bar
    color="primary"
    class="app-bar"
    :height="height"
    fixed
    app>
    <loading />
  </v-app-bar>
</template>

<script>
  import Loading from '@/components/Loading'

  export default {
    name: 'Header',
    components: {
      Loading
    },
    data () {
      return {
        height: '50'
      }
    }
  }
</script>
// ./layouts/default.vue

<template>
    <div>
        <Header />
    </div>
</template>

<script>
import Header from '@/components/Header'

export default {
    components: {
        Header
    }
}
</script>

これでaxios通信が行われている最中にLoadingアニメーションが実行されます。

f:id:honyafan:20211008104441g:plain
axios通信時のLoadingの様子

最後に

axios通信時の手法の肝はLoadingCountという状態を使うことなので、簡単に応用が効くと思います。 axios通信時にローディング処理を入れたいという方は一度試してみてはいかがでしょうか!