改めて学び直すVuex

## まえがき

以前 [Vue.js用のFluxライクなライブラリVuexを試してみる](https://qiita.com/nasum/items/b2f11a392bf8fb125610) という記事を書きました。2年前の記事で更新もしていなかったので改めて整理して学び直してみようと思います。今回使用するバージョンは `3.0.1` です。

## Vuexとは？

VuexはVue.js用の状態管理ライブラリです。Fluxの影響を受けています。ReactだとReduxに当たります。

Fluxの考え方はFacebookのリポジトリにある[flux-concepts](https://github.com/facebook/flux/tree/master/examples/flux-concepts)が詳しいです。その中の図をお借りすると以下のような考え方になります。

![flux.png](https://qiita-image-store.s3.amazonaws.com/0/11510/b18a8c1a-38c4-ccf3-f3d9-a8c24826dbe7.png)[^1]

Action → Dispatcher → Store → View のようにデータが流れていきます。Vuexでもこの考え方と同じようなデータの流れになります。

![vuex.png](https://qiita-image-store.s3.amazonaws.com/0/11510/0d03d653-b2ef-654f-9533-fdd0b4622723.png)[^2]

Vuexでのデータの流れは Actions → Mutations → State → Vue Components となります。それぞれの名前が違うだけでFluxの考え方を素直に取り入れている感じがします。

FluxはコンセプトなのでAPIとの通信は特に指定されていませんが、Vuexはその実装なのでベストプラクティスがあります。図を見ると一目瞭然ですが、ActionsでAPIを叩くように図では指示されています（とはいえFluxの流れをくんでいれば大体Action相当の場所でAPI通信を行うのではないでしょうか）。MutationsではDevtoolsとやりとりを行います。

VuexはVue.jsを使う際Fluxでデータ管理を行いたいとき一番使いやすいライブラリといえるでしょう。Vuexを使用するには、Vue.jsの他のプラグインと同様にVue.jsでインスタンスを作る前に `use` メソッドで登録します。

```javascript
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
```

簡単に使えるのがうれしいところです。

## ストア

Vuexで状態管理を行う場合、まずはじめに作るのはストアです。カウントアップするアプリケーションを例にすると以下のように作成していきます。

```javascript
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  getters: {
    countWithSuffix(state) {
      return `${state.count} 回`
    }
  },
  mutations: {
    increment(state) {
      state.count++
    }
  },
  actions: {
    increment(context) {
      context.commit('increment')
    }
  }
})

const app = new Vue({
  el: '#app',
  computed: {
    showCounter() {
      return this.$store.getters.countWithSuffix
    }
  },
  store,
  methods: {
    increment() {
      this.$store.dispatch('increment')
    }
  }
})
```

```html
<h1>Counter</h1>
<div id="app">
  <div>
    {{ showCounter }}
  </div>
  <button @click="increment">
    increment
  </button>
</div>
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/11510/4f9f9d90-40af-1fd8-37d9-82b2fef39e5e.png)

ストア内には `state` `getters` `mutations` `actions` のプロパティを持ちます。 `state` には状態を、 `getters` には `state` 内の状態を元に算出した値を返す関数を、 `mutations` には `state` のデータを更新する関数を、 `actions` にはミューテーションにデータをコミットする関数を入れます。

それぞれ見ていきます。

### state

`state` にはデータを格納していきます。多くのFluxライクなライブラリと同様に、Vuexも単一のstateに全ての情報を詰め込んでいきます。

`state` の参照は `getters` の関数で行っています。

```javascript
getters: {
  countWithSuffix(state) {
    return `${state.count} 回`
  }
},
```

`getters` に登録した関数の第一引数に `state` が渡されるのでそこから参照します。

`state` の更新は `mutations` の関数で行っています。

```javascript
mutations: {
  increment(state) {
    state.count++
  }
},
```

`mutations` に登録した関数でも第一引数に `state` が渡されるのでそこから値を更新しています。

このように `state` は他の関数に第一引数として渡されて参照と更新を行っていきます。

### getters

`getters` は主に `state` のデータを加工して表示したいときに使用します。今回は `count` の数値に「回」を後ろに付加するデコレータのような役割で使用していますが、ToDoリストなどを作るときに、DONEかそうでないかでフィルタした結果を渡すときなどにも使えます。

`getters` に登録した関数の第二引数に `getters` に登録した他のゲッター関数が渡されます。

```javascript
getters: {
  countWithSuffix(state, getters) {
    return `${getters.countWithPrefix} 回`
  },
  countWithPrefix(state) {
    return `第 ${state.count}`
  }
},
```

このように他のゲッター関数を組み合わせて使用することも可能です。

### mutations

`mutations` は `state` を更新する関数を登録します。 `mutations` 内の関数の呼び出しとして特徴的なことはストアの `commit` 関数を用いて発火させるということです。先に挙げた例では `actions` 内の関数で `commit` を実行していました。

```javascript
actions: {
  increment(context) {
    context.commit('increment')
  }
}
```

`commit` 関数は呼び出す関数の名前（タイプという）を第一引数に入れ、第二引数に該当ミューテーションで使用する値（ペイロードという）を入れることが出来ます。

```javascript
mutations: {
  increment(state, num) {
    state.count += num
  }
},
actions: {
  increment(context) {
    context.commit('increment', 1)
  }
}
```

受け取るミューテーション側の関数も第二引数に値が入ります。この値はオブジェクトを送ることも当然可能です。

さらにもう一つの `commit` の使用の仕方としてオブジェクトをそのまま渡すスタイルもあります。

```javascript
mutations: {
  increment(state, payload) {
    state.count += payload.num
  }
},
actions: {
  increment(context) {
    context.commit({
      type: 'increment',
      num: 1
    })
  }
}
```

`type` には実行する関数名を書き、他のプロパティは渡すデータを登録します。これもミューテーションの関数の第二引数に渡されます。

### actions

`actions` はミューテーションをコミットする関数を登録します。 

```javascript

actions: {
  increment(context) {
    context.commit({
      type: 'increment',
      num: 1
    })
  }
}
```

引数として渡される `context` オブジェクトは、そこを経由して `store` や `getters` も参照することが可能です。ミューテーションの関数を実行するためには `commit` 関数を実行します。 `commit` 関数に渡す引数は `mutations` の節で書いたとおりです。

`actions` の関数を実行するには `dispatch` 関数を Vue のインスタンスで実行します。

```javascript
methods: {
  increment() {
    this.$store.dispatch('increment')
  }
}
```

Vue のインスタンス側からストアを参照し、ストアの `dispactch` 関数を実行します。 `dispatch` 関数も `commit` 関数と同じように実行する関数の名前を指定して実行します。

```javascript
methods: {
  increment() {
    this.$store.dispatch({
      type: 'increment',
      num: 2
    })
  }
}
```

渡されたデータはアクションの関数の第二引数に渡されます。

```javascript
actions: {
  increment(context, payload) {
    context.commit({
      type: 'increment',
      num: payload.num
    })
  }
}
```

このようにしてデータを アクション → ミューテーション → ステート と渡していくことが出来ます。

また、`actions` 内の関数では非同期処理を行うことが可能です。主にアクションの関数内で外部APIの実行し、値を取得しミューテーションに渡してステートを更新していきます。

## まとめ

Vuexの基本的な関数と、それぞれの役割についてカウンターアプリを通して学び直してみました。他にもストアオブジェクトを分割するモジュールという便利な機能があったりします。

今回例で作成したカウンターアプリは以下のURLからコードを見ることが出来ます。実行してみたり改造してみたりしてみてください。

https://jsfiddle.net/tomato360/oxo72sqv/5/

VuexはVueと同様に日本語のドキュメントが充実しています。

https://vuex.vuejs.org/ja/

もしさらに興味を持った場合はこちらをよむと良いと思います。

[^1]: https://github.com/facebook/flux/blob/master/examples/flux-concepts/flux-simple-f8-diagram-with-client-action-1300w.png
[^2]: https://raw.githubusercontent.com/vuejs/vuex/dev/docs/en/images/vuex.png