【初心者向け】状態管理にVuexを使ってみた【入門】

#はじめに
Vue.jsにもちょっとずつ慣れてきて、できるだけコンポーネントを分けるように設計するようになった。

それで親子でデータ連携しようと思ったら
いちいちpropsとかemitとかを記述しないとダメなのでめんどくさいなあと思い始めた。

なので、
一括状態管理できるVuexを使ってみるか〜
って感じで入門してみました。

そもそもVue.jsがよく分からんて人はこれが参考になると思います。
[Vue.js を vue-cli を使ってシンプルにはじめてみる(qiita)](https://qiita.com/567000/items/dde495d6a8ad1c25fa43)

※状態管理ツールにnpmを使用しています。
※vue-cliを使ってvue initした状態です。

※小さいアプリならVuexいらないっていう情報をよく見かけるけど、
個人的にそのアプリが大きくなる可能性がちょっとでもあるなら
Vuexなり、状態管理系は入れといた方がいいと思ってます。
（コンポーネントを分けようとした時にデータ連携部分ですごい負担になる）

#インストール


```bash
npm install vuex
```

※Promiseを使っているらしいのでポリフィルを入れる人は入れてください。
（入れないと特定のブラウザでうまく動作しない可能性があります。）

※参考にした書籍にはbabel-polyfillでOKって書いてました。

```bash
#babel-polyfillを入れる場合
npm install babel-polyfill
```


#最低限動かすために追加・修正が必要なソースたち
##store.js
ここで状態管理をまとめてやります。

※state,getters等の各機能はソース内コメント参照

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

Vue.use(Vuex)

const store = new Vuex.Store({

  //state:コンポーネントでいうdata
  state: {
    message: '初期メッセージ'
  },

  //getters:コンポーネントでいうcomputed的なもの
  getters:{
    //messageを使用するgetter
    message(state) {
      return state.message
    }
  },

  //mutations:コンポーネントでいうmethod（と言うかsetter）
  //stateを唯一変更できるもの
  mutations: {
    //vuexでは引数をpayloadと呼ぶっぽい
    //payloadはオブジェクトにするべき（いっぱい入れれるし）
    setMessage(state,payload){
      state.message = payload.message
    }
  },

  //actionのコミットを使うことでミューテーションを呼び出す（コンポーネントには無い概念）
  actions: {
    doUpdate({commit}, message){
      commit('setMessage',{message})
    }
  }
})
export default store
```


##App.vue

基本的にcomputedでstoreのゲッターを呼んで値を取得するスタイルらしい

```vuejs:src/App.vue
<template>
  <div id="app">
    <p>{{message}}</p>
    <EditForm></EditForm>
  </div>
</template>

<script>
  import EditForm from "./components/EditForm";

export default {
  name: 'App',
  components: {EditForm},

  //vuexのstoreは基本的にcomputedと使う
  computed: {
    //main.jsでローカルにstoreを登録してるので、$storeが使える
    //ここではgettersに登録したmessageゲッターを使ってstoreのstateのmessageを取得している
    message(){
     return this.$store.getters.message
   }
  }
}

</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
```


##main.js

vue initする時にvue-routerを使うってしちゃったので
router関連が入ってますが
無くても動くと思います。多分。

storeをインポートするのを忘れないこと。

```js:src/main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  //storeをローカルに登録
  //これでどこからでも使える
  store,
  router,
  components: { App },
  template: '<App/>'
})
```


##EditForm.vue
状態管理（store）を呼び出したいコンポーネント

ファイル名は各自好きなコンポーネント名を使ってください。


```vuejs:src/components/EditForm.vue
<template>
  <div>
    <!--ここはvalueをv-bindでcomputedのmessageと繋いでる-->
    <!--つまり、入力フォームの初期値にstoreのmessageを使っている-->
    <!--storeのメッセージを表示してるのはApp.vueの方なので注意-->
    <input type="text" :value="message" @input="doUpdate"/>

  </div>

</template>
<script>
export default {
  name:'EditForm',
  computed:{
    message(){
      return this.$store.getters.message
    }
  },
  methods:{
    doUpdate(event){
      //ディスパッチでstoreのアクションを呼んでいる
      //event.targetは押された要素を指すのでここではinputタグを指してる
      //そのvalueなので、event.target.valueは入力された文字
      //つまり、入力された文字を引数として渡してstoreのdoUpdateアクションを呼んでる
      this.$store.dispatch('doUpdate', event.target.value)
    }
  }
}
</script>
```

#メモ

とりあえずこれで動かしたら
テキストボックスが表示されて、
その上にテキストボックス内の文字列がバインドされて表示されてると思います。


##storeのデータを呼び出す大まかな流れ
（１）使いたいコンポーネント（ここではApp.vue）でcomputedを定義する。

（２）computedの戻り値にstoreのゲッターを定義する

（３）あとはデータが欲しいところでcomputedを呼ぶ

##storeのデータを書き込む大まかな流れ
（１）使いたいコンポーネント（ここではEditForm.vue）でディスパッチを使い、storeのアクション（doUpdate）を呼ぶ。引数で値を渡す。

（２）storeのアクションがミューテーション（setterみたいなやつ）を呼ぶ(ミューテーションを呼ぶことをコミットと言うらしい)

（３）storeのミューテーションがもらった引数の値を使ってstoreのデータを変更する



##参考書籍
「基礎から学ぶ Vue.js」
https://cr-vue.mio3io.com/

Vuexの説明めちゃわかりやすかったです。
ありがとうございました。


#終わりに
今回は値を外から読み書きできる最低限の実装やけど、
もっと複雑なこともできるみたい。

本来はAPI叩いたりするのもVuex（のアクション）からやるっぽいのでその辺も今後抑えていきたい。

何か間違ってるとことかあったら優しく指摘してもらえると嬉しいです。