Vuexを用いた中規模以上WebサービスへのVue.jsアーキテクチャリングの導入


この記事は [CBcloud Advent Calendar 2019 11日目](https://qiita.com/advent-calendar/2019/cbcloud)の記事です。

今回は業務で導入したVue.jsとVuexのアーキテクチャについてまとめました。
なおこちらの内容は [Vue.jsアーキテクチャリング勉強会](https://cw-engineers.connpass.com/event/146975/)
にて登壇したものから一部抜粋しまとめたものです。


# 1. Vue.js とは？
https://jp.vuejs.org/

他フレームワークと比較すると一番特徴的なのは、下記の一行だけでVue.jsの機能が利用できることにあると思います。

```html
 <script src="https://cdn.jsdelivr.net/npm/vue"></script>
```

# ２. Vuexとは？
公式サイトにはvuexについてこのように書かれています。
> Vue.js アプリケーションのための 状態管理パターン + ライブラリです。


公式サイトで書かれているVuexを表す図は有名だと思いますが、個人的にはより正確に表すなら下記の図が適していると考えております。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55864/7e27d00c-99da-6a92-ca59-213e7271cd61.png)

ここで抑えておきたいのは、下記の2点です。

- 参照処理はGettersに寄せる
- 更新処理はActionsに寄せる


## なぜGettersとActionsに処理を寄せるのか？

なぜGettersとActionsに処理を寄せるのか？を見る前にVuexの状態変化はそれぞれ下記の役割があります。

- State
    - データの保管先
- Mutations
    - データの変更
- Getters
    - データの取得
- Actions
    - データの更新処理

それぞれ役割を把握した上で、私がGettersとActionsに処理を寄せる理由は下記の2点が大きいと考えています。

- Stateを直接参照すると、Vuexに保存されているデータを何も介さずに見ることになる。Getterなどで表示内容を変更して参照している場合があるため、情報の一意性が保てなくなるため

- Mutationを呼ぶことでvueファイルから、Stateの内容を直接書き換えることができる。これはロジックをvueファイルに内在してしまうので、更新処理はActionsに寄せる必要がある。



### Actions
```ts


export interface RootState {
    version: string;
}

export interface TeamsState {
    teams: Team[]
}

export interface Team {
　   id: number;
    name:string;
    area: Area;
   crestUrl: string;
}

export interface Area {
    id: string;
    name: string;
}
```

### Getters
```js
export const getters = {
  fullName: state => {
    return state.firstName + state.lastName
  },
  array: state => {
    return state.array
  }
}
```

### State
```js
export const state = () => ({
  array: [],
  firstName: ``, 
  lastName: ``,
})
```

### Mutations
```js
export const mutations = {
  [types.SET_ARRAY](state, array) {
    state.array = array 
  },
  [types.SET_FIRST_NAME](state, firstName) {
    state.firstName = firstName
  },
  [types.SET_LAST_NAME](state, lastName) {
    state.lastName = lastName
  }
}
```
### Vueファイル
これらVuexで表されたものをvueファイル側で以下の様に呼び出します。
この規模のコード量であれは冗長的になってしまう様に見えますが、表示に必要な記述とロジックを分離することができています。

```vue
<template>
<components
:data=”data”
></components >
</template>

<script>
import { mapGetters, mapActions} from 'vuex'  

computed: {
...mapGetters(`Store`, [`data`])
},
async fetch({ store, params }) {
 store.dispatch(`Store/getFirstData`)
},
methods: {
   ...mapActions(`Store`, [`update`]),
   async update() {
     await this.update()
   }
}
</script>
```

# 3. より安全に開発する 〜TypeScriptの導入〜

Vue.jsではその名の通り、基本的にはJavaScriptが利用されるフレームワークです。
JavaScriptは動的型付けを採用しており、中規模〜大規模なWebサービスになった場合、
対象のデータがどんなものなのかわからず苦慮することがあります。

[JavaScript のデータ型とデータ構造](https://developer.mozilla.org/ja/docs/Web/JavaScript/Data_structures)


そこでTypeScriptによって静的型付けを行うことで、この負担を軽減します。
実際にTypeScriptの導入を行うことで、IDE内でデータの予測変換が効く様になり、対象のデータがどんなものなのかの想定が楽になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55864/fdcb8a22-1e32-b39d-baa5-31de39722dce.png)



## TypeScriptの導入
TypeScriptではtypesとい型の定義をしたファイルを用意し、Vuexでそれらを利用することができます。


### Actions
```ts
export interface RootState {
    version: string;
}

export interface TeamsState {
    teams: Team[]
}

export interface Team {
　   id: number;
    name:string;
    area: Area;
   crestUrl: string;
}

export interface Area {
    id: string;
    name: string;
}
```

### Actions
```ts
const actions: ActionTree<TeamsState, RootState>= {
  fetch: async ({ commit }) => {
    try {
      const res = await axios.get('https://api.football-data.org/v2/competitions/PL/teams/', {
        headers: {
          'X-Auth-Token': 'XXXX'
        }
      })
      const teams: Team[] = res.data.teams
      commit('teamsLoaded', teams)
      

    } catch (error) {
     // commit('teamsError')
    }  
  },
};

```

### Getters
```ts
const getters: GetterTree<TeamsState, RootState> = {
  teams: (state: TeamsState) => {
    return state
  }
}
```

### State
```ts
const state: TeamsState = {
  teams: [],
};

export const todos: Module<TeamsState, RootState> = {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}
```

### Mutations
```ts

export const mutations: MutationTree<TeamsState> = {
    teamsLoaded(state, teams: Team[]) {
        state.teams = teams
    },
    teamsError(state) {
        state.teams = []
    }
}
```

こちらは実際にVuexをTypeScriptで記載したものになります。ご参考にしていただければ幸いです。
[GitHub](https://github.com/entaku19890818/football-nuxt)

# 4.まとめ

- Vue.jsは他フレームワークと比較すると容易に導入可能
- Vuexは`Vue.js アプリケーションのための 状態管理パターン + ライブラリ`　で下記の様に使うべし
    - 参照処理はGettersによせる
    - 更新処理はActionsに寄せる
- JavaScriptで動的型付けは心理的安全性の高い開発を…

以上ここまで読んでいただきありがとうございました！
私がまとめた中で間違いやこんな内容をもう少し書いてもらいたいなどありましたらお気軽にコメントお願いします！