axios-moduleをやめてaxiosを使う
NuxtMeetup #8
2019/05/07
@nakajmg
じまぐ
@nakajmg
Frontend Engineer at PixelGrid Inc. + 副業
技術書典5でコンポーネント設計の本を出しました
副業で途中参加
- Nuxt.js + Laravel
- フロントを担当
- リリース直前のタイミングで
機能的にはほぼ完成していた
axios-moduleをaxiosに置き換えた
axios-moduleとは
- axiosをthisやstoreのcontextに注入して使えるようにする
- コンポーネントやstoreからhttp(s)通信ができるようになる
async sendForm() {
const url = '/api/users'
const users = await this.$axios.$post(url, this.formData)
this.$router.push('/uses')
}
使用例
async fetch({ store, app }) {
const url = `/api/users/${this.$route.params.id}`
const users = await app.$axios.$get(url)
store.commit('setUsers', { users })
}
使用例
APIとのやり取りが
手軽にできて便利なやつ
なぜaxios-moduleを
使うのをやめたか
アプリケーションの成長を
阻害しそうな要素があった
- .vue + .js : 200ファイル
- 25000行
- APIのエンドポイント数 : 80以上
- Methodごとにカウント
Stats
問題になりそうな箇所
- 同じエンドポイントに対するリクエストが散らばっている
- APIのパスが文字列 or テンプレートリテラルで書かれている
{
async fetch({ app, store, route }) {
const path = `/users/${route.params.id}`
const res = await app.$axios.$get(path)
store.commit('setUserData', { data: res })
},
methods: {
async updateUserData() {
const path = `/users/${this.$route.params.id}`
await this.$axios.$put(path, this.formData)
this.$route.push('/users')
}
}
}
便利な半面、雑に書け(すぎ)る
潜在的な割れ窓
起こりうる問題
- 可読性の低下
- リファクタリングしんどい
- 機能の追加・修正の速度低下
プロジェクトに人を追加したときの初速が出しづらくなったり、負債となる可能性が高い
axios-moduleに限らず起こりうる問題だがthisやcontextに注入されるという性質上、
共通化や抽象化をする余地が少なく負債として顕在化しやすい
やったこと
- axiosをwrapしたリクエストだけを担うモジュールを作成
- 上記モジュールを使ってエンドポイントごとに関数化
- this.$axiosを置き換えてaxios-moduleを削除
抽象化と共通化 (Client-Side Gatewayの作成)
リクエスト用のモジュールを作成
axiosを直接使わずにリクエストだけを行うモジュールを、$axiosと同じようなインターフェースで実装する
今後 sindresorhus/ky に乗り換えたい!となったとしても同じインターフェースを実装すれば交換できる
// src/utils/request.js
import axios from 'axios'
const apiRoot = '/api'
export default {
get(url, config = {}) {
return axios({
method: 'GET',
url: `${apiRoot}${url}`,
...config
})
.then(res => res.data)
.catch(err => err)
},
post(url, config = {}) {
return axios({
method: 'POST',
url: `${apiRoot}${url}`,
...config
})
.then(res => res.data)
.catch(err => err)
},
...
}
エンドポイントの関数化
エンドポイントごとに関数化して、関数ごとにファイルをわける。
1ファイル1関数1export
パスが一緒でもMethodが異なれば別の関数にする
// src/api/updateUserData.js
import request from '~/utils/request'
function updateUserData({ userId, data }) {
const path = `/users/${userId}`
return request.put(path, { data })
}
export default updateUserData
// src/api/getUserData.js
import request from '~/utils/request'
function getUserData({ userId }) {
const path = `/users/${userId}`
return request.get(path)
}
export default getUserData
エンドポイントの関数化
名前をつけることで、パスやMethodを見なくても何をするものか推測できるようになる
- 既存コードを読む負担の軽減
- APIに関するコードの一貫性向上
- APIの変更にも強い
エンドポイントの関数化
エディタの解析が効く!
TypeScriptやJSDocで補完を強化できて圧倒的生産性向上!
// src/api/index.js
import getUserData from '~/api/getUserData'
import updateUserData from '~/api/updateUserData'
export default {
getUserData,
updateUserData
}
$axiosを置き換える
$axiosを使用している箇所を、作成した関数で置き換える
'$axios'で検索してパスとMethodをみて地道に…
import api from '~/api'
export default {
async fetch({ store, route }) {
const res = await api.getUserData({ userId: route.params.id })
store.commit('setUserData', { data: res })
},
methods: {
async updateUserData() {
await api.updateUserData({ userId: this.$route.params.id, data: this.formData })
this.$route.push('/users')
}
}
}
export default {
async fetch({ app, store, route }) {
const path = `/users/${route.params.id}`
const res = await app.$axios.$get(path)
store.commit('setUserData', { data: res })
},
methods: {
async updateUserData() {
const path = `/users/${this.$route.params.id}`
await this.$axios.$put(path, this.formData)
this.$route.push('/users')
}
}
}
置き換えて得られたもの
- コードの可読性・一貫性が上がった
- エディタの解析によって生産性が上がった
- 変更・追加に強くなった
学び
何かを簡単に使えるようにするモジュールやプラグインは、何かを簡単じゃなくしていることもある
学び
「プラグインやモジュールを使わない」ということではなく、アプリケーションの性質や成長に合わせて選定・交換する
おしまい
replace axios-module to axios
By nakajmg
replace axios-module to axios
axios-moduleをaxiosにreplaceしたはなし
- 8,853