Vuex、Vue-router、コンポーネントの基礎がまとめて分かる簡易カウンターアプリを作る

この簡易カウンターアプリを通して、Vuex、Vue-router、コンポーネントの基本的な役割・使い方が学習できたので、これから始める方に少しでも参考になればと思いまとめておきます。

【対象読者】
・Vuex、Vue-router、コンポーネントがそれぞれなんとなくは分かるが、vue-cliで生成されたファイルの中でどう結びつけて使ったら良いかわからない。

・上記３つの基本的な役割・連携のイメージを掴みたい。

【事前準備】
・vue-cliを使える環境にしておく
（参考記事）
[Vue.js を vue-cli を使ってシンプルにはじめてみる](https://qiita.com/567000/items/dde495d6a8ad1c25fa43)
[【Vue.js】Vuexの入り口] (https://qiita.com/kouki-iwahara/items/ff5748db977768fd7669)

・Vuex、Vue-router、コンポーネントの基本構文を大体分かっておく

【環境】
```vue --v3.9.2
node --v12.4.0
```
##アプリの完成図
説明用の為cssは省きましたのでご了承ください。
動作確認の参考までに完成したファイルをgithubに載せておきます。
https://github.com/kouki-iwahara/Counter_App

トップ画面
<img width="865" alt="スクリーンショット 2019-07-12 14.23.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429297/bf46acb7-c681-7861-54a2-ef01579ac36c.png">

カウントアップ画面
<img width="865" alt="スクリーンショット 2019-07-12 18.08.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429297/2158f153-aabe-4caf-56df-d84c809fadbc.png">

カウントダウン画面
<img width="865" alt="スクリーンショット 2019-07-12 18.07.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429297/7ee0757d-b177-c887-64e4-ab3ee6536de5.png">

---
【アプリの内容】
ボタンを押すと表示されているカウントの数値が変わる単純なアプリです。
それぞれのコンポーネントからデータを参照しているカウントは独立して動き、Vuexからデータを参照しているカウントはデータが共有されることがわかります。
Vuexを使ったデータ管理のメリット、コンポーネントを使用すると個々に生成されるのでデータを保持することがわかります。

【大まかな流れ】
カウントアップ・カウントダウンのデータのコンポーネントを作成、そのコンポーネントを登録したファイルをトップページにrouterで結ぶ

---
さっそく作っていきます。
##Vue-cliを立ち上げVuex、Vue-routerを取り入れる
ここの説明は上記の参考記事にお任せします。

##必要なファイルを作る
計５つのファイルを作ります。

**【srcディレクトリの中のcomponentsディレクトリの中に作るファイル】**
(コンポーネント用のファイル２つ)

`UpCounterForm.vue`　→　カウントアップのデータを書く
`DownCounterForm.vue`　→　カウントダウンのデータを書く

**【srcディレクトリの中のviewsディレクトリの中に作るファイル】**
(コンポーネントを登録し、トップページと繋げるファイル２つ)

`CountUp.vue`　→　'UpCounterForm.vue'を登録し、トップページと結ぶ
`CountDown.vue`　→　'DownCounterForm.vue'を登録し、トップページと結ぶ

後でまとめて書きますので４つとも空の状態で良いです。名前も適宜変えてください。

**【Vue-cliで作成されたディレクトリ直下に作るファイル】**
(templateをコンパイルさせる為に必要なファイル１つ)

`vue.config.js `

```javascript:vue.config.js
module.exports = {
  configureWebpack: {
    resolve: {
      alias: {
        'vue$': 'vue/dist/vue.esm.js'
      }
    }
  }
}
```


まだ確認中ですがこれはこの名前でお願いします。
このファイルが無いとtemplateがコンパイルできません。
詳しくはこちらが参考になります。
[Vue2.x系のハマりどころ templateとコンパイラを完全解説するよ](https://aloerina01.github.io/javascript/vue/2017/03/08/1.html)

###App.vueをいじる
```vue
<template>
  <div id="app">
      <h1>カウンター</h1>
  </div>
</template>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
}
</style>
```
**ここまでできたら、一旦localサーバーを再起動させます。**
この作業をしないと`vue.config.js `のファイルが適応されなく、templateコンパイル時にエラーが出ます。

App.vueをいじっておくのは必須では無いですが、反映確認の為、上記に書き換えておくとわかりやすいです。（cssは初期のものを使い回しています）

立ち上げ時この画面になっていたら成功です。（色無いと不自然ですね。。。）
<img width="865" alt="スクリーンショット 2019-07-12 16.31.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429297/0edfa551-b07a-6a7e-3046-27620fd0c3b8.png">
それでは次に進みます！
##【Vuex】　store.jsにデータを設定

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

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    // カウントが共有される数値の初期値
    commonCount: 10
  },
  mutations: {
    multiply(state) {
      state.commonCount = state.commonCount * 2
    },
    divide(state) {
      state.commonCount = state.commonCount / 2
    }
  }
})
```
ここではカウントが共有される数値の初期値と、数値を変える為の処理を実行する関数を設定します。
storeについてはこの記事にまとめました。
[【Vuex】ストアの4つの概念まとめ【唯一の情報源】](https://qiita.com/kouki-iwahara/items/1a75daaa93657b0b56d7)

##コンポーネント用のファイルを書く

```vue:UpCounterForm.vue
<template>
  <div>
    <p>カウントが共有される{{ commonCount }}</p>
    <button @click="multiply">×2</button><br>
    --------<br>
    <p>カウントが独立している{{ count }}</p>
    <button @click="increment">1up</button>
</div>
</template>

<script>
export default {
  name: 'UpCounterForm',
  data() {
    return {
      count: 10
    }
  },
  computed: {
    commonCount() {
      // storeから参照
      return this.$store.state.commonCount
    }
  },
  methods: {
    multiply() {
      // storeから参照
      this.$store.commit('multiply')
    },
    increment() {
      this.count++
    }
  }
}
</script>
```
上記を'DownCounterForm.vue'にコピペし、nameとmethods、それに対応するディレクティブの値を書き換えます。

```vue:DownCounterForm.vue
<template>
  <div>
    <p>カウントが共有される{{ commonCount }}</p>
    <button @click="divide">÷2</button><br>
    --------<br>
    <p>カウントが独立している{{ count }}</p>
    <button @click="decrement">1down</button>
</div>
</template>

<script>
export default {
  name: 'DownCounterForm',
  data() {
    return {
      count: 10
    }
  },
  computed: {
    commonCount() {
      return this.$store.state.commonCount
    }
  },
  methods: {
    divide() {
      this.$store.commit('divide')
    },
    decrement() {
      this.count--
    }
  }
}
</script>
```
これでVuexのstoreのデータとコンポーネントが繋がりました。
次はこのコンポーネントを登録します。

##コンポーネントを登録

```vue:CountUp.vue
<template>
  <div>
    <h2>カウントアップ</h2>
    <up-counter-form/> <!-- コンポーネント'UpCounterForm' -->
    *********************<br>
    <up-counter-form/>
  </div>
</template>

<script>
// componentsの'UpCounterForm'を読み込む
import UpCounterForm from '@/components/UpCounterForm.vue'

export default {
  name: 'CountUp',
  components: {
    UpCounterForm
  }
}
</script>
```
```vue:CountDown.vue
<template>
  <div>
    <h2>カウントダウン</h2>
    <down-counter-form/>
    *********************<br>
    <down-counter-form/>
  </div>
</template>

<script>
import DownCounterForm from '@/components/DownCounterForm.vue'

export default {
  name: 'CountDown',
  components: {
    DownCounterForm
  }
}
</script>
```
これでコンポーネントの登録が終わりです。
比較の為、あえて同じコンポーネントを２つ書いています。

templateにコンポーネントを記述する時は、htmlなのでケバブケースが望ましいです。


カウントアップ・カウントダウンのページができました！

次はこのファイルを、routerを使ってトップページとなるApp.vueと結びます。

##router.jsを書き換える

```javascript:router.js
import Vue from 'vue'
import Router from 'vue-router'
import CountUp from './views/CountUp.vue' //viewsのCountUpを読み込む
import CountDown from './views/CountDown.vue'//viewsのCountDownを読み込む

Vue.use(Router)

export default new Router({
  // 'CountUp'、'CountDown'を結ぶデータを設定
  routes: [
    {
      path: '/countUp',
      name: 'CountUp',
      component: CountUp
    },
    {
      path: '/countDown',
      name: 'CountDown',
      component: CountDown
    }
  ]
})
```
ここで先ほどのコンポーネントしたファイルをrouterに登録します。
ファイルパスに注意してください。

最後に、App.vueを書き換えrouterでリンクを作ります。

```vue:App.vue
<template>
  <div id="app">
    <h1>カウンター</h1>
    <router-link to="/countUp">カウントアップはこちら</router-link>/
    <router-link to="/countDown">カウントダウンはこちら</router-link>
    <router-view/>  
  </div>
</template>

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

以上で完成です。
お疲れ様でした！

##最後に
このアプリを動かしてみます（動かすという程のものでは無いですが、、）。

トップページのリンクをクリックすると、Vue-routerで結ばれたそれぞれ対応しているページに遷移してくれます。

Vuexのstoreからデータを参照した、「カウントが共有される」の場所のボタンを押すと、カウントアップ・カウントダウンのページどちらにもカウントが共有（反映）されていることがわかります。

これは、storeに設定しておいたデータが複数のページに渡り使われても、storeのデータを書き換えるだけで参照していた全てのデータが書き換わることを意味しています。
書き換わる可能性のあるデータをstoreに設定することで情報が保持され、修正コストも下げられるのがVuexの利点です。

また、「カウントが独立している」の場所のボタンを押すと、コンポーネントごとに独立して動いているのがわかります。
これは、コンポーネントがそれぞれ独立して生成されており、情報が保持されていることを意味しています。

このアプリを通して、バラバラに学習していたVuex、Vue-router、コンポーネントの基本的な使い方、役割を繋げることができました。

---
以上になります。
自分自身Vue.jsを学習する時に、この３つの役割をなかなか繋げられず時間がかかったので、これから学習を始める方にとって少しでも参考になれば嬉しいです。
また学習が進み次第、更新、掲載していきます。

ここまでで補足や訂正などありましたら是非ご教授いただけると嬉しいです。
最後まで読んでいただきありがとうございます。