で、Vuexってどうやって使うの？ state, getters, mutations, actions...

# Vuexを使ってログイン状態を管理しよう

## この記事の目的
こんにちは。
現在、Vue + Go を使ってログイン機能を作っている初学者です。
Vue歴1ヶ月です。

初心者なりにVuexで使う `state`, `getters` , `mutations`, `actions` を理解したので、アウトプットしたいと思います。
Vuexの概念というよりは、**どうやって使えば動くのかといったこと**を、初学者の方に見ていただければ幸いです。

間違ってる箇所もあると思いますがご容赦ください。

概念などを詳しく知りたい方は、[公式サイトへ] (https://vuex.vuejs.org/ja/)

## 1.そもそもVuexってなんのためにあるの
公式曰く 
` Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。 `
らしい。

この`状態管理パターン`と`ライブラリ`っていうのがよくわからないが
**`値 ` とか `メソッド` をなどを、他の.vue/.jsファイルからでも取り出すことのできる箱**と自分は認識してます。
どのファイルからでも取り出せるからpropsみたいに依存関係を気にしなくていい。というのがメリットですね。

## 2.はじめに..
まず、npmパッケージでvuexをインストールしてください。

```
 npm install vuex
 ```

Vuexを扱うための箱を作ります。

```store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import router from '../router.js'

Vue.use(Vuex)
export default new Vuex.Store({
})
```

main.jsにも作った箱(store.js)をインポートします。

```main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router.js'
import store from './store/store.js'

new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')
```

ログイン画面を作ります。
ログインの仕組みはid, passwordを入力して、サーバー側にhttpRequestを飛ばすというものです。

サーバー側はidとpasswordを読み取ってDBの値と一致していればログイン成功とし、アクセストークンを返すという作りになっています。
今回はサーバー側の詳細は省きます。

また、ログインした後じゃないとメインページなどにアクセス出来ないような制御をかけます。
(main.jsに後々記載します。)

発火するメソッドに `this.$store.dispatch`という見慣れない記載がありますが、これこそがstore.js内のactionsを呼び出す記法です。

今回はactions内のloginというメソッドを起動しています。
actionsの中でどのような処理が行われているかは後々説明します。


```Login.vue
<template>
  <div>
    <h1>ログイン</h1>
    <lable for="id">Id:</lable>
    <input id="id" placeholder="id" type="text" v-model="id" />
    <br />
    <label for="password">Password:</label>
    <input id="password" placeholder="password" type="text" v-model="password" />
    <button @click="login">ログイン</button>
    <hr />
    <router-link to="/">もどる</router-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      id: "",
      password: "",
    };
  },
    methods: {
    login() {
      this.$store.dispatch('login', {
        id : this.id,
        password: this.password,
      })
    },
  }
};

</script>
```

次にstore.jsに機能を実装していきます。
今回Vuexで使う機能は4つ **state**, **getters**, **mutations**, **actions** です。

## 3.state
stateは、**状態管理を行う機能**です。
要はintや配列などの**値**を**管理**します。

今回はログイン後に作成したアクセストークンと、ログインフラグを保存するためのstateを用意します。


初期値は空とfalseにしています。

```store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import router from '../router.js'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        idToken: "",
        loggedIn: false
    }
})
```

## 4.getters
gettersはstateに格納してある値を、
.vue/.jsファイルなどから呼び出すときに使用します。


条件などを指定して、取り出し方を変更したりもできますが、
今回はそのまま取得します。

```store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import router from '../router.js'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        idToken: "",
        loggedIn: false
    },
    getters: {
        idToken: state => state.idToken,
        loggedIn: state => state.loggedIn
    }
})
```

## 5.mutations 
mutationsはメソッドを管理する機能です。
**stateの情報を変更**するにはこのmutationsを使用します。(ここ重要)

ポイントはmutationsは**常に同期的**に動作するという点です。(ここ重要)
これは次に話す、actionsとの大きな違いです。

ちなみにmutationの 動詞型 : mutate は`変異する,変化する`という意味です。
なので、Vuexではstateの情報を変化させるという意味で、覚えとくと忘れないと思います。

今回は以下の機能を、mutationsに実装します。

1. 引数にidTokenをとる。
2. idTokenの値を更新する。  
3. localStorageにtoken情報を追加。
4. loggedInをtrueにする。

```store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import router from '../router.js'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        idToken: "",
        loggedIn: false
    },
    getters: {
        idToken: state => state.idToken,
        loggedIn: state => state.loggedIn
    },
    mutations : {
        storeIdToken(state, idToken) {
            state.idToken = idToken;
            localStorage.setItem("jwt", JSON.stringify(idToken))
            state.loggedIn = true
        }
    }
})
```

## 6.actions
actionsの説明は公式を引用します。
> - アクションは、状態を変更するのではなく、ミューテーションをコミットします。  
- アクションは任意の非同期処理を含むことができます。

つまりactionsは**mutationsを非同期化する機能しかない**ということです。

actionsは`{commit}`を引数にとり、
内部で`commit(mutations内のメソッド)` を記述することで、mutationsのメソッドを呼び出す事ができます。
第2引数はactionsを呼び出すメソッドから何らかの値をもってくることが出来ます。

今回は、idとpasswordをLogin.vueから持ってきます。
actions内の処理は以下

1. postによって、持ってきたidとpasswordを送ります。(axiosの説明は省略します)
2. responseが返ってきたら、mutationsのstoreIdToken()を起動。
3. VueRouterによって/mainに飛ばします。

``` store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import router from '../router.js'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        idToken: "",
        loggedIn: false
    },
    getters: {
        idToken: state => state.idToken,
        loggedIn: state => state.loggedIn
    },
    mutations : {
        storeIdToken(state, idToken) {
            state.idToken = idToken;
            localStorage.setItem("jwt", JSON.stringify(idToken))
            state.loggedIn = true
        }
    },
    actions: {
        login({commit}, auth){
            axios
            .post("/api/login", {
              id: auth.id,
              password: auth.password,
            })
            .then(response => {
                commit('storeIdToken', response.data.token)
                router.push('main')
            });
        }
    }
})
```  
## 7. 完成

これで後はlocalhostを起動させて、Login.vueのボタンを押せば

1. <span>@</span>clickでLogin.vueのlogin()メソッドが起動
2. 内部のthis.$store.dispatchが起動(id, passwordの情報を持っている)
3. Vuexの機構により、store.jsのactionsが起動
4. サーバー側にid,passowordを送信
5. Requestが成功し、Responseを受け取ったら、mutationsのstoreIdTokenが起動
6. storeIdToken内の処理が起動する。

という流れになります。

```Login.vue
<template>
  <div>
    <h1>ログイン</h1>
    <lable for="id">Id:</lable>
    <input id="id" placeholder="id" type="text" v-model="id" />
    <br />
    <label for="password">Password:</label>
    <input id="password" placeholder="password" type="text" v-model="password" />
    <button @click="login">ログイン</button>
    <hr />
    <router-link to="/">もどる</router-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      id: "",
      password: "",
    };
  },
    methods: {
    login() {
      this.$store.dispatch('login', {
        id : this.id,
        password: this.password,
      })
    },
  }
};

</script>

```


最後にmain.jsにRoutingの制御を実装します。

VueRouterの内容になるので、詳しくは省略しますが、
`store.getters.loggedIn`でloggedInの状態を取ってきてtrueじゃないとログインページに戻されてしまうという感じです。
なので、storeIdtoken()にて、state : loggedInをtrueにしてたというわけです。

``` main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router.js'
import store from './store/store.js'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')

router.beforeEach((to, from, next) => {
  if(to.matched.some(url => url.meta.isPublic) || store.getters.loggedIn) {
      next()
  } else {
      next('login')
  }
});
```

### おまけ

VueRouterの内容がちょこちょこでてきてしまったので、
`router.js`の情報も追記します。

``` router.js
import Vue from "vue";
import Router from "vue-router";

const Home = () => import("./views/Home.vue");
const Header = () => import("./components/Header.vue");
const Login = () => import("./views/Login.vue");
const Main = () => import("./views/Main.vue");

Vue.use(Router);

export default new Router({
  mode: "history",
  routes: [
    {
      path: "/",
      components: {
        default: Home,
        header: Header
      },
      meta: {
        isPublic: true
      }
    },
    {
      path: "/login",
      components: {
        default: Login,
        header: Header,
      },
      meta: {
        isPublic: true
      }
    },
    {
      path: "/main",
      components: {
        default: Main,
        header: Header
      }
    }
  ]
});
```

## 最後に

いかがだったでしょうか。
Vuexのmutationsとactionsの違いがいまいちわからない！って方や
Vueでログイン機能をどうやって実装するのかと考えている方の助けになれば幸いです。

また、今回初めての投稿だったんですが、書き上げるまでに結局2日間もかかっちゃいました...
普段から投稿してくださっている方々の労力はいかに..
いつもありがとうございます。

勉強させていただいたUdemy講義 めちゃくちゃわかりやすいです。
https://www.udemy.com/course/vue-js-complete-guide/

----