7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

なるはや Vue SPA入門:Vuex

Last updated at Posted at 2019-07-20

Vue CLI3 で作成した SPA(Single Page Application)プロジェクト上で、段階的に Vue.js を学んで行きましょう。

目次はこちら

今回は Vuex 編です。

前提事項

Bootstrap 編 が完了していること。

ページの追加

  • Counter.vue を作成
  • router.js を修正
  • App.vue にナビゲーションを追加

やり方を忘れてしまった人は「Vue Router 編」を振り返ってください。

src/views/Counter.vue
<template>
  <div class="counter">
    <h1>Counter</h1>
    <p>{{ count }}</p>
    <div>
      <button
        class="btn btn-sm btn-primary"
        @click="increment"
      >+</button>
      <button
        class="ml-1 btn btn-sm btn-secondary"
        @click="decrement"
      >-</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    }
  }
};
</script>

Counter ページを表示して動作を確認してみてください。+ ボタンを押すとカウントアップされて、- ボタンを押すとカウントダウンします。

Vuex

Vuex はコンポーネントを横断した状態の格納やそれらを操作する為のメソッドを格納する場所です。先程作成したカウンタページを Vuex を使って書き換えてみます。

ステートとミューテーション

src/store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // 状態
  state: {
    count: 0,
  },
  // ストアの状態を変更するメソッド(同期処理のみ)
  mutations: {
    increment(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    },
  },
  // ストアの状態を変更するメソッド(非同期処理もOK)
  actions: {},
})

ステートに count 変数を設け、ミューテーションに incrementdecrement を設けました。ステートはミューテーションを通して変更します。ただし、同期処理に限ります。

src/views/Counter.vue
<script>
export default {
  computed: {
    count() {
      // vuex 内の count ステートを参照
      return this.$store.state.count
    }
  },
  methods: {
    increment() {
      // vuex 内の increment ミューテーションを実行
      this.$store.commit('increment')
    },
    decrement() {
      // vuex 内の decrement ミューテーションを実行
      this.$store.commit('decrement')
    }
  }
};
</script>

vue コンポーネントでは $store 属性で vuex を参照することができます。ミューテーションを実行するには $storecommit メソッドを使用します。

ゲッター

vuex にゲッターを追加してみます。count の内容を2倍して返す double を設けます。

store.js
export default new Vuex.Store({
  // ...
  // ゲッター
  getters: {
    double(state) {
      return state.count * 2
    },
  },
  // ...
})
Counter.vue
<template>
    <!-- ... -->
    <p>count: {{ count }}</p>
    <p>double: {{ double }}</p>
    <!-- ... -->
</template>

<script>
export default {
  computed: {
    count() {
      // vuex 内の count ステートを参照
      return this.$store.state.count;
    },
    double() {
      // vuex 内の double ゲッターを参照
      return this.$store.getters.double;
    }
  },
  // ...
};
</script>

画面の double 欄には count を2倍した値が表示されるはずです。

アクション

アクションはミューテーションと似ていますが、下記の点で異なります

  • アクションは、状態を変更するのではなく、ミューテーションをコミットします
  • アクションは任意の非同期処理を含むことができます

vuex にアクションを追加してみます

store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // ...
  // ストアの状態を変更するメソッド(非同期処理も可、ミューテーションをコミットする)
  actions: {
    increment(context) {
      context.commit('increment')
    },
    // 引数に ES2015 の引数分割束縛を使った例(context を省略できる)
    decrement({ commit }) {
      commit('decrement')
    },
  },
})

アクションのメソッドは context オブジェクトを引数に受け取ります。context.commit を実行することでミューテーションをコミットできます。

Counter.vue
<script>
export default {
  // ...
  methods: {
    increment() {
      // vuex 内の increment ミューテーションを実行
      // this.$store.commit("increment");

      // vuex 内の increment アクションを実行
      this.$store.dispatch("increment");
    },
    decrement() {
      // vuex 内の decrement ミューテーションを実行
      // this.$store.commit("decrement");

      // vuex 内の decrement アクションを実行
      this.$store.dispatch("decrement");
    }
  }
};
</script>

アクションを実行するには $storedispatch メソッドを使用します。

ヘルパー

vue ファイルの中で、毎回 this.$state.xxx と書くの面倒ですよね。そのために、ヘルパーが用意されています。

Counter.vue
<script>
import { mapState } from "vuex"; // ステートのヘルパーをインポート
import { mapGetters } from "vuex"; // ゲッターのヘルパーをインポート
import { mapActions } from "vuex"; // アクションのヘルパーをインポート

export default {
  computed: {
    ...mapState([
      "count" // this.count を this.$store.state.count にマップ
    ]),
    ...mapGetters([
      "double" // this.double を this.$store.state.getters.double にマップ
    ])
    /* DEL mapState, mapGetters で置き換え
    count() {
      // vuex 内の count ステートを参照
      return this.$store.state.count;
    },
    double() {
      // vuex 内の double ゲッターを参照
      return this.$store.getters.double;
    }
    */
  },
  methods: {
    ...mapActions([
      "increment", // this.increment() を this.$sotre.dispatch("increment") にマップ
      "decrement" // this.decrement() を this.$store.dispatch("decrement") にマップ
    ])
    /* DEL mapActions で置き換え
    increment() {
      // vuex 内の increment ミューテーションを実行
      // this.$store.commit("increment");

      // vuex 内の increment アクションを実行
      this.$store.dispatch("increment");
    },
    decrement() {
      // vuex 内の decrement ミューテーションを実行
      // this.$store.commit("decrement");

      // vuex 内の decrement アクションを実行
      this.$store.dispatch("decrement");
    }
    */
  }
};
</script>

ミューテーションやアクションへの引数の渡し方

この記事では例がシンプル過ぎた為、ミューテーションやアクションへの引数の渡し方を取り上げていません。詳細は公式サイトを参照してください。

動作確認

vuex.png

カウントアップ、カウントダウンが正しく動作しているか確認してください。devtools で vuex の内容を確認してみてください。

次回

ライフサイクル

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?