3０分で基礎がしっかりわかる【Vuex】-入門

#はじめに
皆さん、こんにちは！Webシステム開発エンジニアの蘭です！
今日は【Vuex】について語りたいと思います。


##Vuexって何？
>Vuexはすべてのコンポーネントでデータを一元管理するための仕組みです。

##何故Vuex？
Vueで足りるんじゃない？
アプリを構築している中、最初はコンポーネント内だけでdataを操作してたが、コンポネントが多くなってくると、コンポーネントで共有して使うデータが現れます。その時思いついたのが、Vueの$emitやpropsを使うことでコンポーネント間のデータ共有問題を解決してました。

しかしアプリが大きくなるに連れ、コンポーネントが更に多くなり、共有データが300以上に増加、もうこのコンポーネントのデータはどのコンポーネントから持ってきたのかが分からない！:scream:
まさに開発に連れ、地獄の道へと進んでしまってたのです。

その時に、現れたのがVuexでした。

##Vuexっていつ導入すべきなのか？
ここで想像してみましょう。
例えばコンポネントは一つの店だとします。
Aコンポーネントはバナナしか在庫がなく、Bコンポーネントはりんご、Cコンポーネントはスイカ等、それぞれ一種類の果物しか預かってません。

それで各コンポーネントが`フルーツパフェを作りたい時に、コンポーネントAはBにりんごを買ってきたり、BはAからバナナを入荷してましたが`、なにかややこしいですよね。
それでVuexの考え方はコンポネントで`皆使う共有の果物は全部Storeという多きな倉庫に入れといて`、他の果物が欲しいコンポネントは倉庫から入荷しましょうという改善方法が生まれました。
`その倉庫がVuexでの「Store（ストア）」です`。
めでたし、めでたし:smiley:

・Vuexでのデータ管理のイメージ
>共有データを一つのStoreに管理する
![alt](https://reffect.co.jp/wp-content/uploads/2019/06/vuex_props_event-1.png)

##Vuexってどんな時に使うといいの？

>・アプリケーション全体で使用されるデータ→Vuexで管理する
・コンポーネントの内部のみで使用されるデータ→dataオプションで管理する


##実例：Vuexのシンプルなストア（倉庫）
・以下の例はVuexストア（倉庫）から変数countを取得します。
>・コンポーネントからVuexストア共有変数countを`store.state.count`で取得。
・VuexではStoreの共有変数countを直接変更できない為、
・対策：ストア共有変数を変更する関数`increment()`をストア内に準備し、コンポーネントではボタンを押した後、`store.commit('increment')`経由で共有変数countを間接的に更新する。

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="uramaya" data-slug-hash="WNNvOXd" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vuex_Simple_Store_Demo">
  <span>See the Pen <a href="https://codepen.io/uramaya/pen/WNNvOXd">
  Vuex_Simple_Store_Demo</a> by Uramaya (<a href="https://codepen.io/uramaya">@uramaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>



####要するに：ストアと単純なグローバルオブジェクトの違い
>・ストアの状態を直接変更することはできない。明示的にmutationsをコミットすることによってのみストアの状態を変更する。これによりすべての状態変更に追跡可能な記録を残すことが保証される。


##Vuexをやってみよう！

###Vuexのインストール
・通常開発ではnpmやyarnでインスタンスします。
　　※前提：Vueのインストール済み

```sh:vuex_npm_install.
npm install vuex --save
```
```sh:vuex_yarn_install.
yarn add vuex
```

・npmやyarnでvuexをインストールの方、
　以下のVuexを明示的に導入が必要。

```js:import_Vuex.
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
```
・試しであれば、cdnでもOK

```vuex_cdn.
https://cdnjs.cloudflare.com/ajax/libs/vuex/2.0.0/vuex.js
```

###Vuexの概念
以下の内容は[こちら](https://katsuki.hatenablog.com/entry/2018/12/02/231454)を参照しています。
>Vuexのストアは構成要素として5つの概念を持つ
【state】, 【mutations】, 【getters】, 【actions】, 【modules】
以下は上記の5つの概念について説明します。

![alt](https://cdn-ak.f.st-hatena.com/images/fotolife/k/katsuki1207/20181202/20181202231005.png)

####　1.【state】
　　・ストアで管理する状態（共有変数、データ）。コンポーネントのdataみたいにデータを保存する場所。
　　・gettersから参照され、更新はmutationsで行う

>ストアから状態を取り出す`一番シンプルな方法は、[算出プロパティ](https://jp.vuejs.org/v2/guide/computed.html) (computed)で取得しまた返すことです。

・こちらは直接'store.state.count'でストア状態を持ってきてますが、
####・開発で以下の欠点があります。
>モジュールを使うとき、ストアの状態を使っている全てのコンポーネントをインポートしなければなりません。（ストア状態を共有してるモジュールが100個あった場合、地獄に落ちます。）

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="uramaya" data-slug-hash="QWWbqyb" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vuex_Store_Example">
  <span>See the Pen <a href="https://codepen.io/uramaya/pen/QWWbqyb">
  Vuex_Store_Example</a> by Uramaya (<a href="https://codepen.io/uramaya">@uramaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

####・上記解決方法：ルートコンポーネントに store オプションを指定
これですべての子コンポーネントにストアを共有する事ができます。

```js:index.vue
new Vue({
  el: '#app',
   //ルートインスタンスに store オプションを渡す
   //★これをすることで、this.$store で各コンポーネントから参照することができます。
   store,
});
```


<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="uramaya" data-slug-hash="gOOpGxN" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="gOOpGxN">
  <span>See the Pen <a href="https://codepen.io/uramaya/pen/gOOpGxN">
  gOOpGxN</a> by Uramaya (<a href="https://codepen.io/uramaya">@uramaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

####・`mapState` ヘルパー：算出プロパティ(Computed)で宣伝方法の改善
・以下を見るとわかりますが、算出プロパティを全て宣伝するのは冗長です。

```js:Before：元の算出プロパティ(Computed)宣伝方法
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
　　count2 () {
      return this.$store.state.count2
    }
　　count3 () {
      return this.$store.state.count3
    }
   ...
  }
}
```

```js:After：MapState使用後.
computed: mapState({
    count:  'count',  // count: state => state.countと同義
    count2: 'count2', // count2: state => state.count2と同義
    count3: 'count3'  // count3: state => state.count3と同義
  })
```

・npmやyarnでvuexをインストールの方、魔法の言葉を忘れずに

```js:mapState.
import {mapState} from 'vuex'
```


####　2.【getters】
　　・state内の状態をもとに算出した値を返す関数が書かれる場所
　　・stateのデータを加工して表示
　　・state加工するので、最初の引数はstate
　　・状態をフィルタリング、カウントした値を返す

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="uramaya" data-slug-hash="NWWqwPp" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vuex_Store_Example_getters">
  <span>See the Pen <a href="https://codepen.io/uramaya/pen/NWWqwPp">
  Vuex_Store_Example_getters</a> by Uramaya (<a href="https://codepen.io/uramaya">@uramaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>


####　3.【mutations】
　　・stateを更新する関数が書かれる場所
　　・stateの更新はしない
　　・第一引数は必ずstate, それ以降の引数はpayload
　　・state状態を更新する際は必ずcommitを使用
<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="uramaya" data-slug-hash="RwwPzaw" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vuex_Store_Example_mutations">
  <span>See the Pen <a href="https://codepen.io/uramaya/pen/RwwPzaw">
  Vuex_Store_Example_mutations</a> by Uramaya (<a href="https://codepen.io/uramaya">@uramaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

####　4.【actions】
　　・`非同期処理`や外部`API通信`する場所
　　・actionで非同期処理を開始→
　　　実際stateの更新はmutationsをcommitで実行する
　　　　⇛actionでstateの更新は行わない
　　　　⇛最終的にmutationsにデータをコミットする関数
　　　　⇛commitは同期でなければならない

#####Actions処理
以下の例でActions処理の過程を見てみよう。
>全体の処理の流れとしては、コンポーネントからdispatchでアクションを呼び出し、アクション内で外部APIなどからの非同期処理を行った後、commitでミューテーションを使いステートを更新するという流れとなります。
上記は[こちら](https://junpeko.com/vuex-actions/)から引用。

・コンポーネントからストアの処理を呼び出すメモ：
　 MutationsはCommit / Actionsはdispatch

```js:コンポーネント内のmethodsにActionsをdispatchで実行.
 methods: {
    increment(plus) {
　　　//plusは引数
      this.$store.dispatch('incrementAsync', plus);
      this.$store.dispatch('warningAsync');
    }
  },
```

```js:その後、ストアのActionsでcommitを呼び出します.
  actions:{
    incrementAsync({ commit }, plus) {
      setTimeout(()=>{
        alert("これが非同期処理です。");
        commit('increment', plus)
      }, 5000)　//非同期で五秒遅らせる事ができる。
    },
    warningAsync({ commit }) {    
        commit('warning')
    }
  },
```

```js:そして、commitでMutationsからステートを変更します.
  mutations: {
    //★ここでstate状態を変更する関数を用意
    increment (state, plus) {
      state.total += plus
      state.warning_show = false
    },
    warning (state) {
      state.warning = "5秒お待ち下さい。"
      state.warning_show = true
    }
  }, 
```

それでは実装してみよう。
#####`非同期処理`
<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="uramaya" data-slug-hash="jOObNjy" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vuex_Store_Example_actions">
  <span>See the Pen <a href="https://codepen.io/uramaya/pen/jOObNjy">
  Vuex_Store_Example_actions</a> by Uramaya (<a href="https://codepen.io/uramaya">@uramaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>


#####`API通信`
・今回非同期のAPI通信では、ライブラリ「axios」を使います。
・async関数を使ってみる（必須ではない）：
>async関数：
async関数・メソッドはメソッドの冒頭に async をつけることで、 await が使え、awaitは必ずaxios.get等通信処理が終了し、responseもしくはerrorが返ってきた後に、awaitの処理を実行します。

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="uramaya" data-slug-hash="ExxVjym" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="ExxVjym">
  <span>See the Pen <a href="https://codepen.io/uramaya/pen/ExxVjym">
  ExxVjym</a> by Uramaya (<a href="https://codepen.io/uramaya">@uramaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

###　【state】、【getters】、【mutations】、【actions】を使ってTodoListを作ってみよう！
ここまで読んでいただき、ありがとうございます！
ではモジュールを理解する前に、以下のコードで遊んでみてください。

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="arpitg" data-slug-hash="LzEmXe" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue + Vuex Demo">
  <span>See the Pen <a href="https://codepen.io/arpitg/pen/LzEmXe">
  Vue + Vuex Demo</a> by Arpit Gupta (<a href="https://codepen.io/arpitg">@arpitg</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>




####　5.【modules】
　　・上記4つのストア構成要素を分割したもの。
　　・アプリケーションの肥大化に伴い大きくなるストアに対し、見通しをよく保つためにモジュールに分割する。

以下の記事を参考しました。
　　[Vuex公式-モジュール](https://vuex.vuejs.org/ja/guide/modules.html)

>開発に連れ、コードが膨大する中、全て何百個のstate, getters, mutations, actionsを一つのファイルにまとめるのは事実上管理不可能です。
ここで解決法が【モジュール】です。
モジュールとは元々凄く膨大なstateやgetters等を小さなモジュール単位に分割して、管理することです。
もちろん、これで保守、修正、管理が便利になります。

####モジュールの使い方を見てみよう！
#####・1.同一ファイルでモジュール分け
```js:同一ファイルでモジュール分け.
//モジュールA
const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

//モジュールB
const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    module_a: moduleA,
    module_b: moduleB
  }
})

store.state.module_a // -> `moduleA` のステート
store.state.module_b // -> `moduleB` のステート
```

#####・2.違うファイルでモジュール分け（現場）
   ・モジュールの分け方について以下を参考しました。
　　[Vuex の Modules 機能](https://uncle-javascript.com/vuex-modules)
　　[vuexでmoduleを分ける方法と注意点](https://qiita.com/kuriya/items/bc9a070119f0f4bfe944)

######　・(1)モジュール:「superFunction」と「header」を `storeに登録`

```/store/index.js
import Vue from "vue";
import Vuex from "vuex";

import superFunction from "./superFunction";
import header from "./header";

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: {
    superFunction,
    header
  }
});

export default store;
```

######　・(2)モジュールの指定を明確にする為、  `namespaced: true ` にすることを忘れずに
　・namespacedについて以下を引用しました。
　　　[Vuexのストアをモジュールに分割する](https://naito-coding0322.hatenablog.com/entry/2019/01/10/014235)

・Vuex名前空間の概念：【namespaced: true】 とは
>namespaced:オプションをtrueにすることで、それぞれのモジュールに名前空間を与えて呼び出し方を管理することができる。

>ただstateについては、namespaced: trueの有無は関係なく一律で$store.state.(モジュール名).data.messageのように呼び出す。


>その他のmutation, action, getterはnamespaced: trueを与えなかった場合はモジュールを使用せずグローバルに登録したときと同じように呼び出せる。


>もしnamespaced: trueを与えていない複数のモジュール内で名前が被った場合、mutationとactionはそれぞれが同時に実行される。
getterは以下のようなエラーが発生する。

>```
[vuex] duplicate getter key: greetingC.
```
>namespaced: trueを与えたmutation, action, getterは、名前の前にモジュール名/を付与して呼び出すことで、上記のエラーを避けられます。

>```
console.log(this.$store.getters['moduleA/greeting']
console.log(this.$store.commit('moduleA/greeting')
console.log(this.$store.dispatch('moduleA/greeting')
// getterは[], mutationとactionは()で囲むので注意
```

・モジュールを作ってみよう

```/store/superFunction/index.js
const state = {
  appNumber: 0
};

const getters = {
  appNumber(state) {
    return state.appNumber;
  }
};

const actions = {
  changeNumber({ commit }, val) {
    commit("changeNumber", val);
  }
};

const mutations = {
  changeNumber(state, value) {
    state.appNumber = state.appNumber + value;
  }
};

const superFunction = {
  namespaced: true, // 忘れずに
  state,
  getters,
  actions,
  mutations
};

export default superFunction; //モジュールの名前
```






######　・(3)コンポネントでモジュール：superFunctionを使う
>以下の注意点：
・getter と action の参照の仕方が変わる
・this.$store.getters["moduleName/getterName"]
・this.$store.dispatch("moduleName/actionName")

```/components/AppMain.vue
<template>
  <main>
    {{appNumber}}
    <Controller :changeNumber="changeNumber"/>
  </main>
</template>

<script>
import Controller from "./Controller";
export default {
  components: {
    Controller
  },
  computed: {
    appNumber() {
      return this.$store.getters["superFunction/appNumber"];
    }
  },
  methods: {
    changeNumber(val) {
      this.$store.dispatch("superFunction/changeNumber", val);
    }
  }
};
</script>
```

・上記の実際のソースコードを見て試してみよう【Codesandbox】
https://codesandbox.io/embed/w67wklz0pk?fontsize=14

###おまけに
・npmやyarnでVuexをインストールした方は、必ずuseでVuexの参照をしてください。

```/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'
 
Vue.use(Vuex);　//忘れずに
 
const store = new Vuex.Store({
...略...
```
・Vuexで開発する際に、以下の記事も参考にできればと思います。
　　[Vuexを用いた開発プロジェクト用にガイドラインを作成した話](https://qiita.com/HayatoKamono/items/eb6d253a1c73ab001659)
　　[メンテナンスしやすいVueComponentを設計するために気をつけていること](https://aloerina01.github.io/blog/2018-05-01-1)

・VueとVuexの練習
　[30minくらいで学ぶVue.jsとVuex](http://murayama.hatenablog.com/entry/2019/02/13/142555)
　[簡単なTODOアプリで Vue + Vuex を学んでみよう](https://www.webopixel.net/javascript/1240.html)
　[簡単なTODOアプリソースコード（Github）](https://github.com/k-ishiwata/VuexTest/tree/master/src/store)

・Vueの基本概念
　[１０分で基礎がわからるVue.js-入門](https://qiita.com/moyegogo1020/items/7cf9daaacc6bac1b3c33)

##最後に

今回はVuexについて基本の使い方を紹介しました。
自分のニーズに沿って、是非Vuexを開発で使って見てください！:D