Vue.jsのVuexはCounterを作成しながら学習すると理解しやすい

Vuexとは

VuexとはVue.jsアプリケーションのための「状態管理パターン + ライブラリ」

Vue.jsの基礎を習得した人は次にVuexを学習することになるのだが、Vue.jsとは異なりstate, mutations, actions, gettersなどが新しく出てくるので初心者だと習得に苦労することが多い。

しかし、Vuexが難しく感じるのは公式ドキュメントや参考書などの説明が理解しにくいことが原因なので作成しながら順を追って理解すればそれほど難しい内容ではない。

Vue.js Counterの作成手順

繰り返しになるがVuexの習得は実際に手を動かして順番に理解すればそれほど難しい内容ではないので、とりあえずCounterのサンプルを作成してみよう。

以降の説明はvue/cliでVue.jsの基礎を学習済みだがVuexは未習得の人向けです。

yarnを使用しているがnpmを使用している場合はnpmに変えて実行する。

最終的には以下のようなサンプルが完成します。

Vuexで作成したCounterのサンプル

まず、vue create my-counterを実行する。

vue create my-counter

実行後はcd my-counter; yarn serve

cd my-counter; yarn serve

まず、App.vueに+1ボタンを押すとcountが1加算されて、-1ボタンなら減算、そして偶数ならeven、奇数ならoddが表示される処理を書く。

Vue.jsの基礎を習得済みであればdata()、methods、computedは理解できるだろう。

<template>
  <div id="app">
    <p>Clicked: {{ count }} times, count is {{ evenOrOdd }}.</p>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    },
  },
  computed: {
    evenOrOdd() {
      return this.count % 2 === 0 ? 'even' : 'odd'
    }
  }
}
</script>

Vuexを読み込んで使用する

Vuexを読み込んで使用するには事前に以下のコマンドで追加しておく。

yarn add vuex

まず、vuexの処理を書くstore.jsのファイルをmain.jsと同じところに保存して以下のように記述する。

Vuexを使用する際はVueとVuexをimportしてVue.use(Vuex)で使用する。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

store.jsを作成したらmain.js側で読み込むようにしておく。

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

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

data()をstoreに書き換える

Vuexを使用するとdata()ではなくstoreで管理できるようになる。

まずApp.vueのdata()はstore管理に書き換えるので削除する。

次にstore.jsにstateの定数を作成してcountをオブジェクトで用意する。

stateはexport default new Vuex.StoreでApp.vueで使用できるようにする。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
  count: 0
}

export default new Vuex.Store({
  state,
})

次にApp.vueの{{ count }}を{{ $store.state.count }}に書き換える。

こうすることでstore.jsのstate.countがApp.vueに表示できるようになる。

試しにcount: 7のように書き換えれば、{{ $store.state.count }}も7になる。

<template>
  <div id="app">
    <p>Clicked: {{ $store.state.count }} times, count is {{ evenOrOdd }}.</p>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
  </div>
</template>

<script>
export default {
  methods: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    },
  },
  computed: {
    evenOrOdd() {
      return this.count % 2 === 0 ? 'even' : 'odd'
    }
  }
}
</script>

store.jsにmutationsとactionsを追加

mutationsはactionsの値をstateに保存し、mutationsはactionsで実行する。

App.vueのmethodsのincrementはVuexの場合、mutationsとactionsで実行できる。

まずstore.jsにmutationsとactionsを以下のように追加してVuex.Storeにmutationsとactionsを追記する。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
  count: 0
}

const mutations = {
  increment(state) {
    state.count++
  },
  decrement(state) {
    state.count--
  },
}

const actions = {
  increment: ({ commit }) => commit('increment'),
  decrement: ({ commit }) => commit('decrement'),
}

export default new Vuex.Store({
  state,
  mutations,
  actions,
})

App.vueのincrementのthis.count++の代わりがstate.count++の部分だ。

mutationsはactionsでincrement: ({ commit }) => commit('increment'),
}のように記述して実行する。

あとはApp.vueにimport { mapActions } from 'vuex'を読み込んで以下のようにmethodsにmapActionsを追加してstore.jsのactionsを実行できるようにすればボタンを押した際に加算などの処理が動作する。

<template>
  <div id="app">
    <p>Clicked: {{ $store.state.count }} times, count is {{ evenOrOdd }}.</p>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
  </div>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  methods: mapActions([
    'increment',
    'decrement',
  ]),
  computed: {
    evenOrOdd() {
      return this.count % 2 === 0 ? 'even' : 'odd'
    }
  }
}
</script>

computedの部分をgettersに書き換える

最後にcomputedの部分をgettersに書き換える。

store.jsにconst gettersを書いてevenOrOdd()と同じ処理を返す。

actionsと同じくVuex.Storeにgettersの追記が必要なので忘れずに。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
  count: 0
}

const mutations = {
  increment(state) {
    state.count++
  },
  decrement(state) {
    state.count--
  },
}

const actions = {
  increment: ({ commit }) => commit('increment'),
  decrement: ({ commit }) => commit('decrement'),
}

const getters = {
  evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}

export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
})

あとはApp.vue側でmapGettersをvuexからimportしてcomputed部分をmapGettersに書き換えればVuexを使用する前と同じようにボタンを押すことで加算、減算できて、even, oddの処理結果も表示されるようになる。

<template>
  <div id="app">
    <p>Clicked: {{ $store.state.count }} times, count is {{ evenOrOdd }}.</p>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
  </div>
</template>

<script>
import { mapActions, mapGetters } from 'vuex'

export default {
  methods: mapActions([
    'increment',
    'decrement',
  ]),
  computed: mapGetters([
    'evenOrOdd',
  ]),
}
</script>

mapActionsなどでスプレッド演算子を使用

mapActionsとmapGettersはスプレッド演算子を使用して以下のように読み込むこともできる。

methods: mapActionsのように記述すると他にmethodsを追加したいときに困るので、特に理由がなければスプレッド演算子を使用したほうが良い。

export default {
  methods: {
    ...mapActions([
      'increment',
      'decrement',
    ]),
  },
  computed: {
    ...mapGetters([
      'evenOrOdd',
    ]),
  },
}

Vuexで作成したCounterのサンプル