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に注入されるという性質上、

共通化や抽象化をする余地が少なく負債として顕在化しやすい

やったこと

  1. axiosをwrapしたリクエストだけを担うモジュールを作成
  2. 上記モジュールを使ってエンドポイントごとに関数化
  3. 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')
        }
    }
}

置き換えて得られたもの

  • コードの可読性・一貫性が上がった
  • エディタの解析によって生産性が上がった
  • 変更・追加に強くなった

学び

何かを簡単に使えるようにするモジュールやプラグインは、何かを簡単じゃなくしていることもある

学び

「プラグインやモジュールを使わない」ということではなく、アプリケーションの性質や成長に合わせて選定・交換する

おしまい

Made with Slides.com