Vuexのデータフローを理解して使ってみる

Vuexを勉強して、何となく理解できた気がするのでここらでまとめておきます。
#Vuexとは?
Vuexは、Vue.jsの状態管理用ライブラリです。
Vue.jsには、propsという、親テンプレートから子テンプレートへ値を渡すことができる機能が備わっていますが、孫要素やそれよりもさらに下の要素が増えていくと、propsを連鎖させることになってしまい、開発効率が落ちてしまったり管理が大変になるというデメリットがあります。

それを解決してくれるのがVuexです。Vuexをプロジェクトに導入することで、それぞれのコンポーネントは同じ値をVuexのストア内で共有することになるので、必要な時にストアから値を取得するだけでその値を使うことができるようになります。

一般的には中規模以上から使われ始めるようですが、ある程度プロジェクトができあがってから導入するのは移行が面倒なので、規模が大きくなる見込みがあるのなら使った方がよさそうです。

#Vuexのデータフローについて
Vuexは、大きく分けて3つの要素から成り立っています。Actions、Mutations、Stateです。
Actionsが外部APIとの通信、MutationsがStateの書き換え、Stateがプロジェクトの状態管理を担っています。

Vuexでは、下の図のように、コンポーネントがActionsにを呼び出す、ActionsがMutationsを呼び出す、MutationsがStateを書き換える、Stateの値をコンポーネントに送るといった、一方向のデータフローを取っています。
Actionsを使用しない場合はコンポーネントからMutationsを直接呼び出すこともできます。
![データフロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431701/d5002446-70ef-cedf-87e1-f34b6d8121d9.png)

#デモアプリ作成
ここからは、実際にカウンターアプリを作りながら説明していきます。
##プロジェクトの作成
コンソールを開いて、下のように打ち込んでください。

```
vue create counter
```

```Please pick a preset```と表示されたら、
```Manually select features```を選択
そのあとに```Check the features needed for your project```と表示されたら```vuex```をえらんでインストールします。
後はデフォルトのままで大丈夫です。

```
cd counter
yarn serve
```

の後、localhost:8080にアクセスできればプロジェクトの作成は成功です。

##コンポーネントの作成
今回は、デフォルトでプロジェクトに用意されているHelloWorld.vueをそのまま使います。

一旦HelloWorld.vue内に書かれている必要のないコードをすべて消してしまいましょう。

以下のような状態にします。

```vue:src/assets/components/HelloWorld.vue
<template>
  <div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
}
</script>

<style scoped>
</style>
```

一旦Vuexを使わずに作ってみましょう。
このような感じになると思います。

```vue:src/assets/components/HelloWorld.vue
<template>
  <div>
    <p>{{ count }}</p>
    <button type="button" @click="countup">countup</button>
    <button type="button" @click="countdown">countdown</button>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      count: 0
    };
  },
  methods: {
    countup() {
      this.count++;
    },
    countdown() {
      this.count--;
    }
  }
};
</script>

<style scoped>
button {
  margin-left: 20px;
}
</style>
```

![動作確認.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431701/63400c05-75df-8932-d6e3-7be96bb299b0.gif)

countupボタンを押したら1足されて、countdownボタンを押したら1引かれるという機能を作りました。

以降はVuexを使って同じ機能を作っていきます。
##stateの作成
まずは状態を管理するStateを作っていきましょう。
既に```store/index.js```内に一部書かれているので、そのまま使います。
デフォルトではこのようになっていると思います。

```javascript:src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})
```
stateに値を追加しましょう。

```javascript:src/store/index.js
import Vue from 'vue'
   //省略
  state: {
    count: 0
  },
   //省略
```

これをHelloWorld.vueから読み込みます。
stateの値を読み込むには、

```vue
$store.state.値
```
で読み込むことができます。

```vue:src/components/HelloWorld.vue
<template>
  <div>
    <p>{{ $store.state.count }}</p>   <!--変更-->
    <button type="button" @click="countup">countup</button>
    <button type="button" @click="countdown">countdown</button>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
//------------------必要なくなるので削除する-------------
  data() {
    return {
      count: 0
    };
  },
//-----------------------ここまで削除-------------------
  methods: {
    countup() {
      this.count++;
    },
    countdown() {
      this.count--;
    }
  }
};
</script>

<style scoped>
button {
  margin-left: 20px;
}
</style>
```
では、実際の表示を見てみましょう。
![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431701/317b3875-f5d5-fb33-05fb-4eb4144fc552.png)
ボタンを押しても数字は変わりませんが、storeに記述した```count:0```が反映されていると思います。

##mutationの作成
ここからは、mutationを作って、stateの変更ができるようにします。

```js:src/store/index.js
//省略
  mutations: {
    countup: function (state) {
      state.count++;
    },
    countdown: function (state) {
      state.count--;
    }
  },
//省略
```
stateの変更をする場合は、関数の第一引数にstateを渡すのを忘れないようにしてください。
```state.値```で管理されている値を取得できます。

それでは、mutationで作った関数をコンポーネントから呼び出していきましょう。

```vue
$store.commit('関数名')
```
で呼び出すことができます。

```vue:src/components/HelloWorld.vue
<template>
  <div>
    <p>{{ $store.state.count }}</p>
    <button type="button" @click="$store.commit('countup')">countup</button>       <!--変更-->
    <button type="button" @click="$store.commit('countdown')">countdown</button>   <!--変更-->
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
//-------------------必要なくなるので削除する-----------------------
  methods: {
    countup() {
      this.count++;
    },
    countdown() {
      this.count--;
    }
  }
//----------------------ここまで削除-------------------------------
};
</script>

<style scoped>
button {
  margin-left: 20px;
}
</style>
```
確認してみましょう。
![動作確認.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431701/dc2d5eb8-a48c-fb29-120d-0e63b8e86687.gif)
stateが書き換わって、表示されている数字が増えたり減ったりしていることがわかります。

余談ですが、関数の第二引数(payload)に値を渡してあげることで、stateを任意の値に変更することもできます。
##actionsの作成
外部APIとの通信をしないのであればactionsを作る必要はありませんが、mutationsの関数の呼び出し方のみ説明しておきます。
actionsの機能についてもっと知りたい方は[こちら](https://vuex.vuejs.org/ja/guide/actions.html)をご覧ください。

actionsにmutationsの関数を呼び出すための関数を書いていきます。

```js:src/store/index.js
//省略
  actions: {
    countupAction(context) {
      context.commit('countup')
    },
    countdownAction(context) {
      context.commit('countdown')
    }
  },
//省略
```
これでmutationsのcountupとcountdownを呼び出す準備ができました。
このactionsで定義した関数をコンポーネントから呼び出します。

コンポーネントからactionsの関数を呼び出すには、

```vue
$store.dispatch('関数名')
```
で呼び出すことができます。
ボタンクリック時に呼び出せるよう、HelloWorld.vueを書き換えていきます。

```vue:src/components/HelloWorld.vue
<template>
  <div>
    <p>{{ $store.state.count }}</p>
    <button type="button" @click="$store.dispatch('countupAction')">countup</button>      <!--変更-->
    <button type="button" @click="$store.dispatch('countdownAction')">countdown</button>  <!--変更-->
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  methods: {
    countup() {
      this.count++;
    },
    countdown() {
      this.count--;
    }
  }
};
</script>

<style scoped>
button {
  margin-left: 20px;
}
</style>
```
確認してみましょう。
![dsyub-ctrh1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431701/e5c61e42-89cb-ed46-c468-4076c7281e32.gif)
それぞれの関数を呼び出せていることがわかります。

#最後に
Vuexは、初めは理解するのが難しいかもしれませんが、わかるとかなり直感的に操作できます。
規模が大きくなると必須の技術になってくるので、ぜひ使ってみてください。

もしミス等ありましたらコメントまたは編集リクエストでお伝えください。