Vuexでデータとその状態に関する全てを一元管理してみました

1. はじめに

はじめまして、1年半前に入社したベトナム人のドゥックです。

最近担当しているプロジェクトでVueを導入しました。使えば使うほどVueは便利だと思いますが、あることでずっと悩んでいます。それはVueのコンポーネント間でのデータの受け渡しのことです。親から子にデータを渡すためにはpropsを、逆に子から親にデータを渡す時は$emitを使用します。この方法だと構造が一階層の時は問題ないですが、深くネストコンポーネント構造であればデータを共有するのが面倒になってきます。

何か良い方法があるかと探していました。先日先輩に相談してみるとFluxライクなライブラリVuexについて教えてくれました。聞いた瞬間に「これだ!!」と思いました。その時から Vuexを勉強して実際に導入してみました。結果はすごく良くてデータ共有が楽になりました。なので、この記事で皆さんにVuexについてを紹介させていただきたいと思います。

2. Vuexとは

Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。 これは予測可能な方法によってのみ状態の変異を行うというルールを保証し、アプリケーション内の全てのコンポーネントのための集中型のストアとして機能します。

Vuexはネストの深いコンポーネント間でのデータの受け渡しをいい感じにしてくれます。

今まで

f:id:ndduc:20190827104747p:plain

改善後

f:id:ndduc:20190827104833p:plain

Vuexのコアコンセプト

  • ステート(State): ストアで管理している状態で、コンポーネントにおけるdataです。
  • ゲッター(Getter): ストアのステートを取得するための算出データです。コンポーネントでいうcomputed的なものです。
  • ミューテーション(Mutation): ストアのステートを変更する方法です。setterだと考えても大丈夫です。ミューテーションは同期的でなければなりません。
  • アクション(Action): アクションは状態を変更するのではなく、データの加工や非同期処理を行い、その結果をミューテーションを使用してコミットします。

3. 実際にVuexを使ってみる

下記はすでに作った簡単なカウンターアプリにVuexを導入してみます。Vuexのメリットをはっきり見れるためまず導入前のコードの状態を見せます。

導入の前

counter_button.vue

カウンターボタンのコンポーネントです。ボタンをクリックするとcount変数の値が1増えてきます。

<template>
  <button @click="count_up">Count Up</button>
</template>
<script>
  export default {
    data: function () {
      return {
        count: 0
      }
    },
    methods: {
      count_up: function () {
        this.count++;
        this.$emit('count_up', this.count)
      }
    }
  }
</script>

show_count.vue

カウンター数値(countの値)を表示するコンポーネントです。

<template>
  <div>{{count}}</div>
</template>
<script>
  export default {
    props: {
      count: Number
    }
  }
</script>

counter_app.vue

親のコンポーネントです。上記の2子コンポーネントをインポートして出します。

<template>
  <div>
    <counter_button @count_up="count_up"></counter_button>
    <show_count :count="count"></show_count>
  </div>
</template>
<script>
  import CounterButton from "./counter_button";
  import ShowCount from "./show_count";
  export default {
    data: function () {
      return {
        count: 0
      }
    },
    methods: {
      count_up: function (count) {
        this.count = count
      }
    },
    components: {show_count: ShowCount, counter_button: CounterButton}
  }
</script>

main.js

このファイルでVueのインスタンスを初期化します。

import Vue from 'vue/dist/vue.js'
import CounterApp from './counter_app'

new Vue({
  el: '#counter-app',
  components: {
    counter_app: CounterApp
  }
});

毎回ボタンをクリックしたらcountの値が1増えてきて$emitcounter_buttonからcounter_appに渡します。そしてpropcountcounter_appからshow_countに渡して表示します。今後別のデータを共有したいならまた$emittopropを利用しないといけないですね。この面倒を回避するためVuexを導入して次の項目の状態になります。

導入の後

store.js

VuexのStoreを初期化するファイルです。共有したいデータはStoreのステートになります。countステートを変更できるためにincrementミューテーションを追加します。

import Vue from 'vue/dist/vue.js'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state){
      state.count++
    }
  }
})
export default store

counter_button.vue

<template>
  <button @click="count_up">Count Up</button>
</template>
<script>
  export default {
    methods: {
      count_up: function () {
        // $emitの代わりにStoreのアクションを使う
        this.$store.commit('increment')
      }
    }
  }
</script>

show_count.vue

<template>
  // Storeのステートを紐づく
  <div>{{this.$store.state.count}}</div>
</template>
<script>
  export default {
     // propがなくなる
  }
</script>

counter_app.vue

<template>
  <div>
    <counter_button></counter_button>
    <show_count></show_count>
  </div>
</template>
<script>
  import CounterButton from "./counter_button";
  import ShowCount from "./show_count";
  export default {
    components: {show_count: ShowCount, counter_button: CounterButton}
  }
</script>

main.js

import Vue from 'vue/dist/vue.js'
import CounterApp from '../components/counter_app'
import store from '../components/store'

new Vue({
  el: '#about-page',
  // Storeをローカルに登録する
  store,
  components: {
    counter_app: CounterApp
  }
});

見ての通りshow_countpropsがなくなりました。かわりにthis.$store.stateStoreからデータを紐づけます。emitの代わりにthis.$store.commitStoremutationを使ってcountを変更できます。そうするとcounter_app.vueでコードがスッキリになって今後新しいデータを共有したい時に単にStoreに追加してコンポーネントの中で紐付ければすぐに使えます。すごく便利じゃないですか?

4. 終わりに

Vuexを使うことで状態管理ができ、propsと$emitを繰り返し使用するといった面倒から開放されました。実際に担当しているプロジェクトで導入するため2日間Vuexを勉強して、6時間導入して合計が22時間ぐらいです。これからの便利さに考えるとこれは必要なことだと思いませんか?

皆さんも是非Vuexを使ってみてください!!!