vue.js＋Vuexチュートリアル

vue-cli環境構築資料
https://gist.github.com/bora-apo/4f9b25e3631818a32077a0a912402ac5#file-vue-cli-build-md


第1回Vue.js勉強会資料
https://gist.github.com/bora-apo/4f9b25e3631818a32077a0a912402ac5#file-vue_stady1-md


この資料は2017/4/25に社内で開催した、第2回Vue.js勉強会の資料です。
今回はVue.jsと一緒に使われるライブラリ「Vuex」について試してみます。

# vuex
Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。 
https://vuex.vuejs.org/ja/intro.html

# 状態管理パターンとは
![vuex勉強会＿1-01.png](https://qiita-image-store.s3.amazonaws.com/0/48488/66bf1648-4f42-da9a-bd18-64563b2f2119.png)

https://jsfiddle.net/pocchi/ttw0u72L/

![vuex勉強会＿1-02.png](https://qiita-image-store.s3.amazonaws.com/0/48488/5833089d-507c-d5b4-c24d-dd0a130962fd.png)

3つの役割に分けることができる。
一方向の流れで管理がシンプルだが、複数のコンポーネントで情報源を共有しようとすると値が重複し、管理が面倒になる。


![vuex勉強会＿1-03.png](https://qiita-image-store.s3.amazonaws.com/0/48488/301c0557-6da3-1fd1-34bf-71234c2f9dfc.png)

そこで、情報源を1つに集約しようとするのが　Vuexです。

今回は簡単な入力フォームを作成します。
vue.jsを使わなくても作れますが、練習用に作成します。

### 画面の流れ

![vuex勉強会-03.png](https://qiita-image-store.s3.amazonaws.com/0/48488/2caf7749-bfa2-9b60-2cca-5e434b33edc6.png)

![vuex勉強会-04.png](https://qiita-image-store.s3.amazonaws.com/0/48488/7566740c-c4b3-96b4-0b6d-dfc5722391eb.png)

## 画面の作り
                               
 
![vuex勉強会-05.png](https://qiita-image-store.s3.amazonaws.com/0/48488/6fb898e6-5c7c-3224-db96-b1173e01a4a2.png)

- h1のヘッダは使いまわせそう。
- エラー文は出しわけが発生する
- テキストエリアと確認文の切り替え
- ボタンの文字も2種類文字がある

## 作成の方法
![vuex勉強会.png](https://qiita-image-store.s3.amazonaws.com/0/48488/8598c0e8-5300-facb-d273-2de800cfd269.png)

これを1コンポーネントで作成します。


# 準備とか

まず、vuexを使用するので、インストールします。
`npm install --save vuex`
![スクリーンショット 2017-04-23 0.30.00.png](https://qiita-image-store.s3.amazonaws.com/0/48488/0f6184d7-558b-862f-29cd-1842b3638788.png)


![スクリーンショット 2017-04-22 20.22.36.png](https://qiita-image-store.s3.amazonaws.com/0/48488/40bb01d7-ef63-0e73-13ba-66da9a31acd4.png)

`npm run dev`
で立ち上がったデフォルトの画面です。こんな感じになっているかと思います。

### src/component/Hello.vue

デフォルトの画面を作っているのがここ。

```js
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h2>Essential Links</h2>
    <ul>
      <li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
      <li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
      <br>
      <li><a href="http://vuejs-templates.github.io/webpack/" target="_blank">Docs for This Template</a></li>
    </ul>
    <h2>Ecosystem</h2>
    <ul>
      <li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
      <li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
      <li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'hello',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>
```

これをコピーして進めていきます。
`src/components/Hello.vue`をコピーします。
`src/components/Form.vue`という名前にしましょう。

### src/components/Form.vue
必要な部分を残して消します。
`<template></template>`の中身を削除しちゃいましょう！
適当に書き換えてみます。

```diff
<template>
-  <div class="hello">
-    <h1>{{ msg }}</h1>
-    <h2>Essential Links</h2>
-    <ul>
-      <li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
-      <li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
-      <li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter -Chat</a></li>
-      <li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a>--</li>
-      <br>
-      <li><a href="http://vuejs-templates.github.io/webpack/" target="_blank">Docs for This Template</a></li>
-    </ul>
-    <h2>Ecosystem</h2>
-    <ul>
-      <li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
-      <li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
-      <li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
-      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
-    </ul>
-  </div>
+ <div>Formページ</div> 
</template>

<script>
export default {
-  name: 'hello',
+  name: 'form',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>
```
このForm.vueを大元のコンポーネントとして使用していきます。
しかし、これだけでは表示はまだHello.vueのままです。
ルーティングを書き換えます。

### src/router/index.js

```diff
import Vue from 'vue'
import Router from 'vue-router'
- import Hello from '@/components/Hello' //コンポーネントを読み込む
+ import Form from '@/components/Form'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/', //パスを指定
-      name: 'Hello',
-      component: Hello // 上で読み込んだコンポーネントを指定 
+      name: 'Form',
+      component: Form 
    }
  ]
})
```

書き換えると、こうなります。
ちなみに、ページリロードは自動でしてくれるので、更新をする必要はありません。

![スクリーンショット 2017-04-22 22.54.23.png](https://qiita-image-store.s3.amazonaws.com/0/48488/3d929838-675b-3c39-d7a7-794c04df2913.png)

ロゴも残ってしまっているので、削除しましょう。
### src/App.vue
`<img src="./assets/logo.png">`を削除します

```diff
<template>
  <div id="app">
-   <img src="./assets/logo.png">
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'app'
}
</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>
```
これで消えました。
ここまでは以下にコードがあります。
https://bitbucket.org/Pocchi/vuex-study/commits/f518b6320d14799716e71146e93995e183f797a1


## モジュールを作成します
今回はわかりやすいように`components/modules/`ディレクトリを作成します。 その下に以下のファイルを作っていきます。

- modules/HeadComp.vue
- modules/TextareaComp.vue
- modules/StringComp.vue

`src/components/Form.vue`をコピーして、それぞれの名前をつけておきましょう！
(`src/components/Form.vue`はそのまま！消しちゃだめ)

### src/components/moduls/HeadComp.vue

```diff
<template>
- <div>Formページ</div>
+ <h1>{{title}}</h1>  
</template>

<script>
export default {
- name: 'form',
+ name: 'headComp',
  data () {
    return {
-     msg: 'Welcome to Your Vue.js App'
+     title: '感想を入力'
    }
  }
}
</script>
```

HeadComp.vueをFormに登録します
### src/components/Form.vue

```diff
<template>
- <div>Formページ</div>
+  <div>
+    Formページ
+    <HeadComp></HeadComp>
+  </div>

</template>

<script>
+ import HeadComp from '@/components/modules/HeadComp'
export default {
  name: 'form',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
- }
+ },
+ components: {
+   HeadComp
+ }
}
</script>
```
こうなっているはず！
![スクリーンショット 2017-04-22 23.49.29.png](https://qiita-image-store.s3.amazonaws.com/0/48488/daead640-64c5-6171-7898-d945684c1b2c.png)

[コード]
https://bitbucket.org/Pocchi/vuex-study/commits/3ea694640dd813364578f1a9fe22f89b9f195787

### src/components/modules/TextareaComp.vue

```diff
 <template>
-  <div>Formページ</div>
+  <div>
+    <p class="error">{{error}}</p>
+    <textarea></textarea>
+  </div>
 </template>
 
 <script>
 export default {
-  name: 'form',
+  name: 'textareaComp',
   data () {
     return {
-      msg: 'Welcome to Your Vue.js App'
+      error: '入力は必須です'
     }
   }
 }
</script>
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped>
-h1, h2 {
-  font-weight: normal;
-}
-
-ul {
-  list-style-type: none;
-  padding: 0;
-}
-
-li {
-  display: inline-block;
-  margin: 0 10px;
-}
-
-a {
-  color: #42b983;
+.error {
+  color: red;
 }
 </style>
```
### src/components/Form.vue
```diff
<template>
   <div>
     Formページ
     <HeadComp></HeadComp>
+    <TextareaComp></TextareaComp>
   </div>
 </template>
 
 <script>
 import HeadComp from '@/components/modules/HeadComp'
+import TextareaComp from '@/components/modules/TextareaComp'
+
 export default {
   name: 'form',
   data () {
     return {
       msg: 'Welcome to Your Vue.js App'
     }
   },
   components: {
-    HeadComp
+    HeadComp,
+    TextareaComp
   }
 }
 </script>

</script>
```
こうなっているはず
![スクリーンショット 2017-04-23 0.02.11.png](https://qiita-image-store.s3.amazonaws.com/0/48488/d5cfcbd9-fdf9-93e3-ea0d-4d34cad31849.png)
[コード]
https://bitbucket.org/Pocchi/vuex-study/commits/0566b5d42783f82bd1d0af8ba8b7a0ce6d8e3962

### src/components/modules/StringComp.vue

```diff
<template>
-  <div>Formページ</div>
+  <p>{{string}}</p>
 </template>
 
 <script>
 export default {
-  name: 'form',
+  name: 'stringComp',
   data () {
     return {
-      msg: 'Welcome to Your Vue.js App'
+     string: '入力された感想をここに出す'
     }
   }
 }
 </script>
```
### src/components/Form.vue
```diff
<template>
   <div>
     Formページ
     <HeadComp></HeadComp>
     <TextareaComp></TextareaComp>
+    <StringComp></StringComp>
   </div>
 </template>
 
 <script>
 import HeadComp from '@/components/modules/HeadComp'
 import TextareaComp from '@/components/modules/TextareaComp'
+import StringComp from '@/components/modules/StringComp'
 
 export default {
   name: 'form',
   data () {
     return {
       msg: 'Welcome to Your Vue.js App'
     }
   },
   components: {
     HeadComp,
-    TextareaComp
+    TextareaComp,
+    StringComp
   }
 }
 </script>
```

![スクリーンショット 2017-04-23 0.13.30.png](https://qiita-image-store.s3.amazonaws.com/0/48488/5d598fdb-9f74-64e0-9279-eed213124cc1.png)

[コード]
https://bitbucket.org/Pocchi/vuex-study/commits/67daf522eb0659b13e17c66dfd70c8f69db2543d?at=topic/re_form

# buttonの作成
### src/components/Form.vue 

```diff
<template>
   <div>
     Formページ
     <HeadComp></HeadComp>
     <TextareaComp></TextareaComp>
     <StringComp></StringComp>
+    <button v-on:click="buttonAction">{{button}}</button>
   </div>
 </template>
 
 <script>
 import HeadComp from '@/components/modules/HeadComp'
 import TextareaComp from '@/components/modules/TextareaComp'
 import StringComp from '@/components/modules/StringComp'
+import { mapActions, mapGetters } from 'vuex'
 
 export default {
   name: 'form',
   data () {
     return {
-      msg: 'Welcome to Your Vue.js App'
+      button: '確認'
     }
   },
+  methods: mapActions('Form', {
+    'buttonAction': 'buttonAction'
+  }),
   components: {
     HeadComp,
     TextareaComp,
     StringComp
   }
 }
</script>
```
mapActionsというのはvuexのactionを使うためのもの。
vuexでは、storeという状態管理を集約したモジュールを作成する。
storeはmain.jsに注入することで、その子となるテンプレートで使用できる。

### src/main.js

```diff
// 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',
   router,
   template: '<App/>',
-  components: { App }
+  components: { App },
+  store
 })

```

### src/store/index.js
src/store/index.jsを作成する。

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

Vue.use(Vuex)

const Form = {
    namespaced: true,
    state: {},
    mutations: {},
    actions: {
      buttonAction({ commit, state, rootState })  {
      console.log("buttonAction")
      }
   }
}

export default new Vuex.Store({
  modules: {
    Form
  }
})
```
![スクリーンショット 2017-04-23 0.41.12.png](https://qiita-image-store.s3.amazonaws.com/0/48488/fdf90018-3875-6f4b-5bc6-e24d574d3f3f.png)
確認ボタンを押すと、コンソールに`buttonAction`と出てきます。

# データをstoreへ集約する
データが分散しています。管理を楽にするためにstoreへ集めます。

## vuex構成
- getter  ・・・テンプレートへ値を返します
- state   ・・・値を保持する
- mutation・・・stateの値を変更する
- action  ・・・mutationを呼び出す

![vuex (1).png](https://qiita-image-store.s3.amazonaws.com/0/48488/4178b5ea-8f88-f7b1-7703-7aeefdeb7a17.png)



## HeadCompのtitle
ボタンを押したら変更できるようにしましょう

### src/components/modules/HeadComp.vue 

```diff
 <template>
   <h1>{{title}}</h1>
 </template>
 
 <script>
+import { mapGetters } from 'vuex'
+
 export default {
   name: 'headComp',
-  data () {
-    return {
-      title: '感想を入力'
-    }
-  }
+  computed: mapGetters({
+  'title': 'getTitle'
+  })
 }
 </script>
```

###  src/store/index.js
```diff
import Vue from 'vue'
 import Vuex from 'vuex'
 import router from '../router'
 
 Vue.use(Vuex)
 
 const Form = {
     namespaced: true,
     state: {},
     mutations: {},
     actions: {
       buttonAction({ commit, state, rootState })  {
         console.log("buttonAction")
+        commit('setStepCount', null, {root: true})//rootへのアクセス
       }
    }
 }
 
+const Head = {
+  state: {
+    title: ["感想を入力", "確認画面", "送信完了"]
+  },
+  mutations: { },
+  actions: { },
+  getters: {
+    getTitle (state, getters, rootState) {
+      return state.title[rootState.stepCount]
+    }
+  }
+}
+
 export default new Vuex.Store({
+  state: {
+    stepCount: 0
+  },
+  mutations: {
+    setStepCount (state) {
+      console.log("rootsetStepCount")
+      state.stepCount++
+    }
+  },
   modules: {
-    Form
+    Form,
+    Head
   }
 })
```
![スクリーンショット 2017-04-23 1.08.51.png](https://qiita-image-store.s3.amazonaws.com/0/48488/dd85412a-3e46-23a5-3bc7-abddada4ae9c.png)

[コード]
https://bitbucket.org/Pocchi/vuex-study/commits/b9fe2316787348526bd135550c84a50d233bb2f9#Lsrc/store/index.jsF10T10

## buttonを送信へ切り替え

### src/components/Form.vue

```diff
<template>
   <div>
     Formページ
     <HeadComp></HeadComp>
     <TextareaComp></TextareaComp>
     <StringComp></StringComp>
     <button v-on:click="buttonAction">{{button}}</button>
   </div>
 </template>
 
 <script>
 import HeadComp from '@/components/modules/HeadComp'
 import TextareaComp from '@/components/modules/TextareaComp'
 import StringComp from '@/components/modules/StringComp'
 import { mapActions, mapGetters } from 'vuex'
 
 export default {
   name: 'form',
-  data () {
-    return {
-      button: '確認'
-    }
-  },
   methods: mapActions('Form', {
     'buttonAction': 'buttonAction'
   }),
+  computed: mapGetters('Form', {
+    'button': 'getButton'
+  }),
   components: {
     HeadComp,
     TextareaComp,
     StringComp
   }
 }
 </script>
```
### src/store/index.js
```diff
 import Vue from 'vue'
 import Vuex from 'vuex'
 import router from '../router'
 
 Vue.use(Vuex)
 
 const Form = {
     namespaced: true,
-    state: {},
+    state: {
+      button: ["確認", "送信"],
+    },
     mutations: {},
     actions: {
       buttonAction({ commit, state, rootState })  {
         console.log("buttonAction")
         commit('setStepCount', null, {root: true})//rootへのアクセス
       }
-   }
+    },
+  getters: {
+    getButton (state, getters, rootState) {
+      return state.button[rootState.stepCount]
+    }
+  }
 }
/* 省略 */
```
[コード]
https://bitbucket.org/Pocchi/vuex-study/commits/43699e83fac2e72d557af137eb2a1643e08b8cd7?at=topic/re_form

## formの制御
テキストエリアの入力があればエラー文を消す

### src/components/modules/TextareaComp.vue
```diff
<template>
   <div>
     <p class="error">{{error}}</p>
-    <textarea></textarea>
+    <textarea v-model="impression"></textarea>
   </div>
 </template>
 
 <script>
+import { mapGetters } from 'vuex'
+
 export default {
   name: 'textareaComp',
-  data () {
-    return {
-      error: '入力は必須です'
-    }
+  computed: {
+    impression: {
+      get () {
+        return this.$store.state.impression
+      },
+      set (value) {
+        this.$store.commit('updateImpression', value)
+      }
+    },
+    ...mapGetters('Textarea', {
+      'error': 'getError'
+    })
   }
 }
 </script>
```
### src/store/index.js
```diff
/* Head以下に追加 */
+const Textarea = {
+  namespaced: true,
+  state: {
+    errorMsg: "入力は必須です",
+  },
+  getters: {
+    getError (state, getters, rootState) {
+      if (rootState.errorFlag) {
+        return null
+      } else {
+        return state.errorMsg
+      }
+    }
+  }
+}
+
 export default new Vuex.Store({
   state: {
-    stepCount: 0
+    stepCount: 0,
+    impression: "",
+    errorFlag: false//trueなら通過
   },
   mutations: {
     setStepCount (state) {
       console.log("rootsetStepCount")
       state.stepCount++
+    },
+    updateImpression (state, value) {
+      state.impression = value
+      if (state.impression) {
+        state.errorFlag = true
+      } else {
+        state.errorFlag = false
+      }
     }
   },
   modules: {
     Form,
-    Head
+    Head,
+    Textarea
   }
 })
```
![スクリーンショット 2017-04-23 1.34.50.png](https://qiita-image-store.s3.amazonaws.com/0/48488/b3db0226-864d-656b-ff29-bc600863e71d.png)
[コード]
https://bitbucket.org/Pocchi/vuex-study/commits/3ef5063678e62775edd59dc8235f4e8a9004bcb4?at=topic/re_form

### 確認ボタンを押してテキストエリアを切り替え
### src/components/Form.vue
```diff
<template>
   <div>
     Formページ
     <HeadComp></HeadComp>
-    <TextareaComp></TextareaComp>
-    <StringComp></StringComp>
+    <component
+    :is="isComponent"
+    ></component>
     <button v-on:click="buttonAction">{{button}}</button>
   </div>
 </template>
 
 <script>
 import HeadComp from '@/components/modules/HeadComp'
 import TextareaComp from '@/components/modules/TextareaComp'
 import StringComp from '@/components/modules/StringComp'
 import { mapActions, mapGetters } from 'vuex'
 
 export default {
   name: 'form',
   methods: mapActions('Form', {
     'buttonAction': 'buttonAction'
   }),
   computed: mapGetters('Form', {
-    'button': 'getButton'
+    'button': 'getButton',
+    'isComponent': 'getComponent'
   }),
   components: {
     HeadComp,
     TextareaComp,
     StringComp
   }
 }
 </script>
```
### src/components/modules/StringComp.vue 

```diff
<template>
   <p>{{string}}</p>
 </template>
 
 <script>
+import { mapGetters } from 'vuex'
+
 export default {
   name: 'stringComp',
-  data () {
-    return {
-     string: '入力された感想をここに出す'
-    }
-  }
+  computed: mapGetters('String', {
+    'string': 'getString'
+  })
 }
 </script>
```

### src/store/index.js
```diff
 import Vue from 'vue'
 import Vuex from 'vuex'
 import router from '../router'
 
 Vue.use(Vuex)
 
 const Form = {
     namespaced: true,
     state: {
       button: ["確認", "送信"],
+      component: ["TextareaComp", "StringComp"]
     },
     mutations: {},
     actions: {
       buttonAction({ commit, state, rootState })  {
         console.log("buttonAction")
-        commit('setStepCount', null, {root: true})//rootへのアクセス
+        if (rootState.errorFlag) {
+          commit('setStepCount', null, {root: true})//rootへのアクセス
+        }
       }
     },
   getters: {
     getButton (state, getters, rootState) {
       return state.button[rootState.stepCount]
+    },
+    getComponent (state, getters, rootState) {
+      return state.component[rootState.stepCount]
     }
   }
 }
 
 const Head = {
  /* 省略 */
 }
 
+const String = {
+  namespaced: true,//名前空間を有効にする
+  getters: {
+    getString (state, getters, rootState) {
+      return rootState.impression
+    }
+  }
+}
+
 export default new Vuex.Store({
   state: {
     stepCount: 0,
     impression: "",
     errorFlag: false//trueなら通過
   },
   mutations: {
     setStepCount (state) {
       console.log("rootsetStepCount")
       state.stepCount++
     },
     updateImpression (state, value) {
       state.impression = value
       if (state.impression) {
         state.errorFlag = true
       } else {
         state.errorFlag = false
       }
     }
   },
   modules: {
     Form,
     Head,
-    Textarea
+    Textarea,
+    String
   }
 })
```
![スクリーンショット 2017-04-23 1.53.04.png](https://qiita-image-store.s3.amazonaws.com/0/48488/d913467e-096a-0966-f122-03a02a6c46ae.png)

[コード]
https://bitbucket.org/Pocchi/vuex-study/commits/a44142a8fb0d0091de20488012b44d95e303c50e?at=topic/re_form

# 送信後のthanksページを作成します
`src/components/Thanks.vue`を作成します。
### src/components/Thanks.vue
```js
<template>
  <div>
    Thanksページ
    <HeadComp></HeadComp>
    送信ありがとうございました！
  </div>
</template>

<script>
import HeadComp from '@/components/modules/HeadComp'
import { mapActions, mapGetters } from 'vuex'

export default {
  name: 'thanks',
  components: {
    HeadComp
  }
}
</script>
```

### src/router/index.js
```diff
import Vue from 'vue'
 import Router from 'vue-router'
 import Form from '@/components/Form'
+import Thanks from '@/components/Thanks'
 
 Vue.use(Router)
 
 export default new Router({
   routes: [
     {
       path: '/',
       name: 'Form',
       component: Form
+    },
+    {
+      path: '/thanks',
+      name: 'Thanks',
+      component: Thanks
     }
   ]
 })
```
### src/store/index.js
```diff
import Vue from 'vue'
import Vuex from 'vuex'
import router from '../router'
 
Vue.use(Vuex)

const Form = {
     namespaced: true,
     state: {
       button: ["確認", "送信"],
       component: ["TextareaComp", "StringComp"]
     },
     mutations: {},
     actions: {
       buttonAction({ commit, state, rootState })  {
         console.log("buttonAction")
         if (rootState.errorFlag) {
           commit('setStepCount', null, {root: true})//rootへのアクセス
         }
+        if (rootState.stepCount == 2) {
+          router.push('thanks')
+        }
       }
     },
/* 以下略 */
```
![スクリーンショット 2017-04-23 2.15.42.png](https://qiita-image-store.s3.amazonaws.com/0/48488/1a187670-26b9-f4aa-934c-6bc84b0e2947.png)

[コード]
https://bitbucket.org/Pocchi/vuex-study/commits/f1cc369888ed65aaeb0d432a7d5e9b81e87e2a62?at=topic/re_form


# おわり
動きましたでしょうか？

サーバーサイドレンダリングについては触れませんでした。
https://ja.nuxtjs.org/
というものがあるそうです。
使ってみたら、また共有したいと思います。