この記事について
- Vueコンポーネント内やVuexのAction内などあちこちでAxiosによるAPIリクエストをしていると、エラーハンドリングを個別に記述しなければならなくなり、DRYではなくなってしまう
- APIリクエストでエラーが発生した場合に特定のページに遷移させたいのに、Vueインスタンスの外からは$router.push()が呼べない
この記事では、上記のよくある問題をなるべくDRYかつシンプルに解決した方法を述べます。
検証環境
- Vue2系(Options API、でもcomposition APIでも動くと思う)
- Vuex(今回の実装には関係ないけれど、Vuexを組み合わせる事も出来る)
- Vue Router
- JavaScript
APIリクエストを1箇所で管理する
参考:https://kntmr.hatenablog.com/entry/2018/02/28/200112
各所でAxiosをそのまま用いて非同期通信をすると、それぞれでエラーをcatchしなければなりません。それは辛いので、別にモジュールを用意してAxiosをラップした関数を定義し、その中でエラーハンドリングを行います。基本的に参考サイトのままですが、関数の部分だけ、get, postなどメソッドで分けずに、configで指定するようにしています。
import axios from 'axios'
const debug = process.env.NODE_ENV !== 'production'
const onSuccess = (resp) => {
if (debug) {
console.log(' << ' + JSON.stringify(resp.data))
}
return Promise.resolve(resp.data)
}
const onError = () => {
throw new Error('API error.')
}
// リクエストメソッド問わず同じ関数を用いる
const api = (config) => {
if (debug) {
console.log(`${config.method} ${config.url} >> ${config.params}`);
}
return axios(config)
.then(onSuccess)
.catch(onError);
};
export default api;
コンポーネントやVuexでaxios()を使っていた箇所をapi()に置き換えることで、エラーハンドリングの記述が1箇所・1回で済みます、イッツDRY。
エラー時にVue Routerでページ遷移
上記まででエラーハンドリングは1箇所で管理することが出来ました。
さてその際、APIアクセスが失敗した時に、Vue Routerの特定にページに遷移させたい時があります。なので、上記の/src/api/index.js
のonError()
で、$router.push()したい訳です。
ですがコンポーネントの外(=Vueインスタンスの外)から$router.push()は使うことが出来ません。なのでちょっと工夫します。
Vueインスタンスのexport
Vueプロジェクトのエントリーポイントである/src/main.js
は、Vue CLIでプロジェクトを生成した際は下記のようにVueインスタンスを生成・HTMLを描画しています。
// 略
new Vue({
render: h => h(App),
}).$mount('#app')
ここで、Vueインスタンスをエクスポートします。
// 略
const vm = new Vue({
render: h => h(App),
}).$mount('#app')
export default vm;
ここでexportしているvm
は、Vueインスタンスのシングルトンです。どこでimportしても常に同一のインスタンスを参照します。
参考:https://qiita.com/NeGI1009/items/f8b17d856a4b15b1ecbc
なので/src/main.js
をimportすればどこからでもVueインスタンスにアクセス出来る訳です。
とはいえ、様々な場所で無秩序にVueインスタンスを操作するのは避けるべきと考えます。
apiモジュールでVueインスタンスにアクセス
import axios from 'axios'
// Vueインスタンス
import vm from '../main';
//略
const onError = () => {
//エラー時にルートへ遷移
vm.$router.push('/')
throw new Error('API error.')
}
//略
この時、Vuex APIにもRouterと同様、vm.$storeでアクセス出来ます。つまりVuexに保存した変数を取得出来るし(getter)、setterやactionを発火させる事も出来ます。
以上でAPIリクエスト時のエラーハンドリングの集中管理およびエラー時のページ遷移が実現しました。あちこちに散逸しがちなエラーハンドリングに対する悪くない実装パターンではないでしょうか。