技術メモなど

業務や日々のプログラミングのなかで気になったことをメモしています。PHP 成分多め。

Vuexのstate/getters/mutations/actionsについて調べた

JavaScript関連の基礎学習なう。
今回はVuexのステート・ゲッター・ミューテーション・アクションについて調べた。

ストアインスタンスの生成

Vuexを使用するには、まずストアオブジェクトを作成し、Vueインスタンスに登録する。
ストアオブジェクトの状態は、stateプロパティで持つことができる。
登録されたストアオブジェクトは、this.$storeを介してVueインスタンス内のすべてのコンポーネントから参照できる。

// main.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from './components/App.vue';

// Vuexの使用を宣言
Vue.use(Vuex);

// ストアインスタンスの生成
const store = new Vuex.Store({
  // 状態はstateプロパティで保持する
  state: {
    count: 1,
  }
})

new Vue({
  // Vueに作成したストアインスタンスを登録する。
  store,
  render: h => h(App),
}).$mount('#app')
// App.vue
<template>
  <div>
    {{ count }}
  </div>
</template>

<script>

export default {
  computed: {
    count () {
      return this.$store.state.count // 1
    }
  }
}
</script>

ストアオブジェクトを使っている状態でも、本来のdataプロパティはローカルステートとして利用できる。

// App.vue
<template>
  <div>
    {{ storeCount }}
    {{ localCount }}
  </div>
</template>

<script>

export default {
  data: function(){
    return {
      count: 11,
    }
  },
  computed: {
    storeCount () {
      return `store count: ${this.$store.state.count}` // store count: 1
    },
    localCount () {
      return `local count: ${this.count}` // local count: 11
    },
  }
}
</script>

getters

コンポーネントで共通のcomputedメソッドがあるとき、共通化の手段としてgettersオプションが提供されている。 gettersに登録された関数の結果はキャッシュされ、その関数が依存しているstateの値が変更されたときにのみ再評価される。

定義したゲッターは、$store.gettersからアクセスできる(プロパティスタイルアクセス)。
また、ゲッターの戻り値を関数にすることで、ゲッター自身に引数を渡せるようになる(メソッドスタイルアクセス)。ただし、メソッドスタイルアクセスのゲッターについては結果はキャッシュされず、実行のたびに計算が行われる。

// main.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from './components/App.vue';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 1,
  },
  // getterの定義
  getters: {
    // stateオブジェクトには、第一引数からアクセスできる
    storeCount (state) {
      return `store count: ${state.count}`
    },
    // 関数を戻り値にすることで、getterに引数を渡せるようになる。
    totalCount(state) {
      return function(localCount){
        return `total count: ${state.count + localCount}`
      };
    }
  }
})

new Vue({
  store,
  render: h => h(App),
}).$mount('#app')
// App.js
<template>
  <div>
    {{ storeCount }}
    {{ localCount }}
    {{ totalCount }}
  </div>
</template>

<script>

export default {
  data: function(){
    return {
      count: 11,
    }
  },
  computed: {
    storeCount () {
      return this.$store.getters.storeCount // store count: 1
    },
    localCount () {
      return `local count: ${this.count}` // local count: 11
    },
    totalCount () {
      return this.$store.getters.totalCount(this.count) // total count: 1
    },

  }
}
</script>

mutations

ストアの状態は、ミューテーションによってのみ変更される。 ミューテーションはタイプハンドラを持ち、ハンドラ関数は直接実行することができない。 実行するためには、store.commitをタイプを指定して呼び出す必要がある。 また、ハンドラ関数は非同期処理を行なってはならない

// main.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from './components/App.vue';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 1,
  },
  getters: {
    storeCount (state) {
      return `store count: ${state.count}`
    },
    totalCount(state) {
      return function(localCount){
        return `total count: ${state.count + localCount}`
      };
    }
  },
  // ミューテーションの定義
  mutations:{
    // stateオブジェクトには、第一引数からアクセスできる
    // 第二引数から、commitに渡された値を受け取れる。
    increment(state, payload){
      state.count += payload.num;
    }
  }
})

new Vue({
  store,
  render: h => h(App),
}).$mount('#app')
// App.vue
<template>
  <div>
    {{ storeCount }}
    {{ localCount }}
    {{ totalCount }}
    <input type="button" @click="storeCountIncrement" value="increment">
  </div>
</template>

<script>

export default {
  data: function(){
    return {
      count: 11,
    }
  },
  computed: {
    storeCount () {
      return this.$store.getters.storeCount // store count: 1
    },
    localCount () {
      return `local count: ${this.count}` // local count: 11
    },
    totalCount () {
      return this.$store.getters.totalCount(this.count) // total count: 1
    },

  },
  methods: {
    storeCountIncrement(){
      // 第一引数にタイプを指定する。引数が必要な場合は第二引数に指定する。
      this.$store.commit('increment', {num: 1});

    //   commitには以下のように、オブジェクトを渡すこともできる
    //   this.$store.commit({
    //     type: 'increment',
    //     num: 1
    //   });
    }
  }
}
</script>

actions

アクションもミューテーションと同じくタイプハンドラを持ち、ハンドラ関数の実行にはstore.dispatchをタイプを指定して呼び出す。
ミューテーションとの違いは以下。 - 自身は状態を変更しない。変更するにはミューテーションをコミットする。 - 任意の非同期処理を含むことができる。

// main.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from './components/App.vue';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 1,
  },
  getters: {
    storeCount (state) {
      return `store count: ${state.count}`
    },
    totalCount(state) {
      return function(localCount){
        return `total count: ${state.count + localCount}`
      };
    }
  },
  mutations:{
    increment(state, payload){
      state.count += payload.num;
    }
  },
  actions: {
    // 第一引数にはコンテクストオブジェクト(後述)が渡され、ここからcommitできる。
    // 第二引数から、dispatchに渡された値を受け取れる。
    increment(context, payload){
      context.commit('increment', payload);
    }
  }
})

new Vue({
  store,
  render: h => h(App),
}).$mount('#app')
<template>
  <div>
    {{ storeCount }}
    {{ localCount }}
    {{ totalCount }}
    <input type="button" @click="storeCountIncrement" value="increment">
  </div>
</template>

<script>

export default {
  data: function(){
    return {
      count: 11,
    }
  },
  computed: {
    storeCount () {
      return this.$store.getters.storeCount // store count: 1
    },
    localCount () {
      return `local count: ${this.count}` // local count: 11
    },
    totalCount () {
      return this.$store.getters.totalCount(this.count) // total count: 1
    },

  },
  methods: {
    storeCountIncrement(){
     // 第一引数にタイプを指定する。引数が必要な場合は第二引数に指定する。
      this.$store.dispatch('increment', {num: 1});

    //   commitと同じく、オブジェクトを渡すこともできる
    //   this.$store.dispatch({
    //     type: 'increment',
    //     num: 1
    //   });
    }
  }
}
</script>

本文中のコードは以下にまとめてあります。 Vuex調査

参考文献

Vuex - ステート
Vuex - ゲッター
Vuex - ミューテーション
Vuex - アクション