Vue.js 3 入門 「Vuex」

# はじめに

Vue.js 3 の Vuex について、自分が学んだことを備忘録として記載します。
Vue.js に殆ど触れたことが無い方に少しでも参考になれば幸いです。
誤り等あれば、ご指摘頂けますと大変喜びます

# Vuex とは

Vuexはアプリで利用するデータを、一箇所に集中管理するためのライブラリです。
管理だけではなく、データの操作（更新）方法も標準化することができます。コンポーネント間でのデータ共有がシンプルに実現できるようになりますね。

# 今回のお題

今回は、数字をカウントできる簡単なアプリを作成してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/f762f2ba-7bf2-ba41-6818-aeacf982a6f2.png)

# プロジェクトの作成

まずは Vue CLI を用いてプロジェクトを作成します。
Vue CLI については[こちらの記事](https://qiita.com/yagi405/items/4e498ef424b92d9d38d0)を参照してください。

プロジェクトを作成するには、作成したいフォルダで以下のコマンドを実行します。
`hello-vuex`はプロジェクト名です。任意のプロジェクト名を設定してください。

```
cd 任意のフォルダ
vue create hello-vuex
```

## プリセットの選択

すると、以下のように利用するプリセット（プロジェクト設定）の選択を求められます。
まずは最低限の構成とするので「Manually select features」（手動で選択）を選択します。
versionはご自身のバージョンに読み替えてください。

```
Vue CLI v4.5.9
? Please pick a preset:
  Default ([Vue 2] babel, eslint)
  Default (Vue 3 Preview) ([Vue 3] babel, eslint)
> Manually select features    
```

## プロジェクトに組み込むモジュールを選択

プロジェクトに組み込むモジュールを選択します。
ここで`Babel`と`Linter`に加えて、`Vuex`を選択します。
[Space]キーで選択することができ、[Enter]キーで確定となります。

`Vuex`を選択することによって、`Vuex`というアプリで利用するデータの、集中管理機能を提供するライブラリが組み込まれます。

```
Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project:
 (*) Choose Vue version
 (*) Babel
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 ( ) Router
>(*) Vuex
 ( ) CSS Pre-processors
 (*) Linter / Formatter
 ( ) Unit Testing
 ( ) E2E Testing                                                                                                                                                                                                                                                                   
```

## Vue.js のバージョンを選択

Vue.js のバージョンを選択します。
本記事では 3.x を選択します。

```
Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Vuex, Linter
? Choose a version of Vue.js that you want to start the project with
  2.x
> 3.x (Preview)                                                                                                                                                                                              
```

## Linter の設定を選択

`Linter` の設定を選択します。
今回は最低限の`ESLint with error prevention only`（エラー防止のみ）を選択します。

```
Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Vuex, Linter
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Pick a linter / formatter config: (Use arrow keys)
> ESLint with error prevention only
  ESLint + Airbnb config
  ESLint + Standard config
  ESLint + Prettier                                                                                                                                                                                                                                                                                                          
```

続けて、Lintの実行タイミングの選択を求められます。
`Lint on save`（保存時）を選択します。

```
Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Vuex, Linter
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Pick a linter / formatter config: Basic
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) Lint on save
 ( ) Lint and fix on commit      
```

## 設定情報の格納先を選択

`Babel`と`ESLint`の設定情報を個別の設定ファイルとするか、`package.json`にまとめるかを選択します。
個別の設定ファイルとしたほうが綺麗なので`In dedicated config files`を選択します。

```
Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Vuex, Linter
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
> In dedicated config files
  In package.json 
```

## 今回の設定を保存しておくかを選択

今回の設定を保存しておくかを選択します。
今回はあくまでお試しなので`N`（保存しない）とします。

```
Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Vuex, Linter
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N) N                                                                                                                         
```                    

## プロジェクトの生成開始

ここまでの設定内容を元に、プロジェクトの生成が開始されるので、完了するまで待機します。
正常に完了すると、以下のような文言が表示されます。

```
Vue CLI v4.5.9
Creating project in 任意のフォルダ\hello-vuex.
Installing CLI plugins. This might take a while...

途中省略...

Running completion hooks...

Generating README.md...

Successfully created project hello-vuex.
Get started with the following commands:

 $ cd hello-vuex
 $ npm run serve
```

## 生成されたフォルダを確認

カレントフォルダに、指定したプロジェクト名のフォルダが生成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/d1d04756-e80f-7080-2ce2-2a9c3c874e5c.png)

# アプリの実行

早速実行してみましょう。
上記のプロジェクト生成完了時の文言（Get started with the following commands:）にある通り、以下のコマンドを実行します。
プロジェクトルートに移動して、開発用のサーバーを実行するコマンドです。

```
cd hello-vuex
npm run serve
```

以下のような文言が表示されれば、開発用のサーバーが起動できています。
ブラウザを起動し`http://localhost:8080`にアクセスしてください。

```
  App running at:

途中省略...

  Note that the development build is not optimized.
  To create a production build, run npm run build.
```

# 動作確認

以下のような画面が表示されれば、プロジェクトの作成は成功です。
開発用サーバーは[Ctrl] + [C]で終了することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/5e1c719f-a222-f2a5-7560-9549367aefe3.png)

# ストアの定義

まずはストアを定義します。
ストアは主に、データと、データを更新するためのメソッド で構成されます。プロジェクトを作成した時点で、空のストア定義（`/src/store/index.js`）が用意されているので、こちらを編集していきます。

## `/src/store/index.js`

```js
import { createStore } from 'vuex'

export default createStore({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})
```

ストアは`createStore`メソッドで定義することができます。

## createStoreメソッド

### 引数

* defs
    * ストアを構成する要素（ステート、ミューテーション、etc...）を`要素名:定義`の形式で記述します
    * 複数の要素を定義する際は`，`区切り

## ステートの定義

ステートとは、ストアで管理されるデータの本体です。
ステートで管理すべき情報を`名前: 初期値`の形式で定義します。（複数の場合は`，`区切り）

今回は、カウンターを示す`count`を定義します。

### `/src/store/index.js`

```js
import { createStore } from 'vuex'

export default createStore({
  state: {
    count: 0 //追加
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})
```

## ミューテーションの定義

Vuexでは、ステートを専用のメソッド経由で更新します。
ステートの更新フローが限定されるので、コードの見通しが良くなります。

このような、ステートを更新するためのメソッドのことを、ミューテーションと呼びます。
今回はステート`count`を1増やす`increment`メソッドと
`count`を1減らす`decrement`メソッドを定義します。

ミューテーションは引数にステート（`state`）を受け取るので、実際の各情報には`state.名前`とするとアクセスできます。

```js
import { createStore } from 'vuex'

export default createStore({
  state: {
    count: 0
  },
  mutations: {
    increment(state){
      state.count += 1
    },
    decrement(state){
      state.count -= 1
    }
  },
  actions: {
  },
  modules: {
  }
})

```

# ストアの有効化

なお、ストアは`/src/main.js`で有効化されています。

```js
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

createApp(App).use(store).mount('#app')
```

Vueインスタンスにライブラリを組み込む`use`メソッドに、定義したストア（`store`）を渡すことで有効化しています。

# ストアにアクセスする

## ステートの表示

実際にコンポーネントからストアにアクセスしてみましょう。

まずはストアに定義したステート`count`の値を画面に表示してみます。
ステートには、`this.$store.state.データの名前`でアクセスできます。
`/src/App.vue`を以下のように修正します。

```vue
<template>
  {{count}}
</template>

<script>
export default {
  name: 'App',
  computed:{
    count(){
      return this.$store.state.count
    },
  }
}
</script>
```

実際の画面を確認すると、初期値に指定した0が表示されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/a5f1c4d7-e8d4-694b-f9e2-0eef26244cb7.png)

## ミューテーションの呼び出し

次に、ステート`count`の値を増減できるようにしてみます。
ステートを増減させるには、先程定義したミューテーションを呼び出します。
ミューテーションは`this.$store.commit(ミューテーション名)`で呼び出すことができます。
`/src/App.vue`を以下のように修正します。

```vue
<template>
  <input type="button" v-on:click="ondecrement" value="-" />
  {{count}}
  <input type="button" v-on:click="onincrement" value="+" />
</template>

<script>
export default {
  name: 'App',
  computed:{
    count(){
      return this.$store.state.count
    },
  },
  methods:{
    onincrement(){
      this.$store.commit('increment')
    },
    ondecrement(){
      this.$store.commit('decrement')
    }
  }
}
</script>
```

実際の画面を確認すると、[+]ボタンと[-]ボタンが増えており、
ステートの値を増減させることができるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/d612045a-518c-f997-cedc-af9fce5657b7.png)

以上となります。
ありがとうございました。
他の機能（ゲッター/アクション/etc...）については別の記事にします。