LoginSignup
209
219

More than 3 years have passed since last update.

【Vuex】ストアの4つの概念まとめ【唯一の情報源】

Last updated at Posted at 2019-07-09

Vuexの根幹 【ストア】

【役割】
Vuexを使う上でアプリケーションの状態(情報)を保持する役割です。

Vuexのコンセプトである『Vuexは信頼できる唯一の情報源である事』が前提にあるため、ストアはアプリケーションの中でただ一つだけの根幹となる存在です。
今回はその大事な役割であるストアの4つの概念をまとめます。

4つの概念

・state→アプリケーションの状態(情報)
・getter→stateの一部やstateから返された値を保持する
・mutation→stateを更新(変化)させる
・action→非同期通信や外部APIとのやりとりを行う

この4つを一纏めにしたものをモジュールと言います。
モジュールでこの4つを守っているイメージです。
502-vue-useful-link_vuex.png

ストアの作成

Vue-cliでVuexを取り込んでおけば、srcフォルダにstore.jsが入っています。
Vuexも読み込まれていますので、そちらを使います。
まだ準備ができていない方はこちらの記事を参考にしてみてください。
【Vue.js】Vuexの入り口

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

Vue.use(Vuex)

const store = new Vuex.Store({ //説明の為、代入しました
  //ここに実装を書きます
})

export default store

説明のため少しいじりました。この状態から始めます。

state

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

Vue.use(Vuex)

const store = new Vuex.Store({
  //stateオプションで初期値を設定
  state: {
    count: 10
  }
})
//store.stateで参照
console.log(store.state.count) //10

export default store

【役割】
アプリケーション全体の状態を保持します。

stateはdataオプションのような存在で、stateが変更されるとコンポーネントの算出プロパティやテンプレートへと反映されます。
これはVuexがVueのリアクティブシステムを活用して実装されている為です。
state更新時にUIの再描画が必要最小限に留まり、開発コストが下がります。

【参照】
store.state.state名

【注意点】
なんでもstateに情報を保持させるのではなく、コンポーネント内部でしか使わないようなものはこれまでと同様にdataオプションに、アプリケーション全体で管理するものはstore内で管理すると良いです。

getter

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

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 10
  },
  //gettersオプションで定義
  getters: {
    //stateから別の値を計算
    squared: function(state) {
      return state.count * state.count
    },
    //他のgettersの値を使うことも可能 (省略技法ももちろん使える)
    cubed: (state, getters) => state.count * getters.squared
  }
})

console.log(store.state.count) //10
//store.gettersで参照
console.log(store.getters.cubed) //1000

export default store

【役割】
stateから別の値を算出する為に使われます。

gettersは算出プロパティcomputedと同様な働きをしてくれます。
値がキャシュされ、そのgettersが依存しているstateが更新されない限り再評価しません。
違う点は引数にstateと他のgettersを持つことで、それらを使って違う値を返します。

【参照】
store.getters.getters名

【引数】
state
他のgetters

【注意点】
getterを参照した時に定義した関数が常に発火する訳ではありません。
computedと同様に、計算した値を返す以外の処理は行うべきではありません。

mutation

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

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 10
  },
  //mutationsオプションで定義
  mutations: {
    // 'increment'mutationを定義
    increment: function(state, payload) {
      state.count += payload.amount
    }
  }
})

console.log(store.state.count) //10
// store.commitでmutationを呼び出す
store.commit('increment', { amount: 5 })
console.log(store.state.count) //15

export default store

【役割】
stateの値を更新する為に使われます。

【呼び出し】
store.commit('mutation名', payload)

【引数】
mutation→ 第一引数: state(渡されたstateを更新)
commit→ 第二引数: payload
(store.commitの第二引数をpayloadと呼び、これを使用することで同じmutationで異なる値にstateを変更できます。)

【注意点】
原則として、mutation以外でstateの更新を行うことを禁止しています。
stateの状態がいつどこで発生したのかを追跡しやすくする為です。
また、全て同期的な処理にする必要があります。
非同期処理を用いると特定のケースで意図しない動作を行ってしまう可能性がある為です。
非同期処理を行いたい時はactionsで定義します。

action

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

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 10
  },
  mutations: {
    increment: function(state) {
      state.count += 1
    }
  },
  // actionオプションで定義
  actions: {
    incrementAction: function(ctx) {
      // 'increment'mutationを実行
      ctx.commit('increment')
    }
  }
})

console.log(store.state.count) //10
// store.dispatchでactionを呼び出す
store.dispatch('incrementAction')
console.log(store.state.count) //11

export default store

【役割】
非同期処理や外部APIとの通信を行い、最終的にmutationを呼び出す為に使われます。

【呼び出し】
store.dispatch.action名

【引数】
ctx(context)

contextとは特別なオブジェクトで、次の内容が含まれています。
・state: 現在のstate
・getters: 定義されているgetter
・dispatch: 他のactionを実行するメソッド
・commit: 他のmutationを実行するメソッド

【注意点】
mutation同様に、直接呼び出すことはできません。
また、dispatchを使うことによって共通の処理を一つのactionにまとめる事ができますが、使いすぎるとどのactionから呼ばれているのか分かりづらくなってしまいます。

非同期処理の例

Ajaxでデータを取得し、そのデータをpayloadに含めたmutationを呼び出すactionを定義している例です。

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

Vue.use(Vuex)

// 例として非同期処理を行う関数
function getCountNum(type) {
  return new Promise(resolve => {
    // 1秒後にtypeに応じたデータを返す
    setTimeout(() => {
      let amount
      switch (type) {
        case 'one':
          amount = 1
          break;
        case 'two':
          amount = 2
          break;
        case 'ten':
          amount = 10
          break;
        default:
          amount = 0
      }
      resolve({ amount })
    }, 1000);
  })
}

const store = new Vuex.Store({
  state: {
    count: 10
  },
  mutations: {
    increment: function(state, payload) {
      state.count += payload.amount
    }
  },
  // actionオプションで定義
  actions: {
    incrementAsync: function({ commit }, payload) {
      // 非同期にデータを取得
      return getCountNum(payload.type)
        .then(data => {
          commit('increment', {amount: data.amount})
      })
    }
  }
})
//後ほど下記に書き換えます
store.dispatch('incrementAsync', {type: 'one'}) //後ほど下記に書き換え

action内でPromiseを返している場合、store.dispatchの戻り値のPromiseを使い、action内の非同期処理の完了を検知できます。
上記はaction内でPromiseを返しているので、下記のようにstore.dispatchの戻り値のPromiseに対してthenでコールバックしactionを完了を検知しています。

store.js
//上記に追記
console.log(store.state.count) //10
store.dispatch('incrementAsync', {type: 'one'})
  .then(() => {
    //actionの処理が完了した後に実行される
    console.log(store.state.count) //11
  })
export default store

今回は以上です。
また学習が進み次第、更新、掲載していきます。
最後まで読んでいただきありがとうございます。

209
219
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
209
219