11
7

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 3 years have passed since last update.

Vuexでモジュール分割 〜ファイルを分けてみた〜

Last updated at Posted at 2020-10-30

Vuexを使っていてstateが多くなってきたのでモジュール化したいと思い、せっかくなので各モジュールを1ファイル毎に分けてみました。

モジュール化の設定と、コンポーネントからの利用方法をメモしておきます。
全部公式ページに書いてあることですが、特に呼び出し方なんかは、毎回「どう書くんだったけ??」と探すのが面倒だったのでまとめてみました。

環境

  • vue: 2.6
  • vuex: 3.5

ファイルを分ける

さっそく各モジュールを1ファイに分けていきます。
今回はVuexの公式サイトの例をベースに利用したいと思います。

構成

ディレクトリ構成はこんな感じにします。

store
├── index.js
├── modules
│   ├── moduleA.js
│   └── moduleB.js

moduleA.jsファイルを作る

moduleA.js
const getDefaultState = () => ({
  message: '',
  count: 0,
})

export default {
  namespaced: true,
  state: getDefaultState(),
  mutations: {
    message(state, value) {
      state.message = value
    },
    increment(state) {
      state.count++
    },
    reset(state) {
      Object.assign(state, getDefaultState())
    },
  },
  getters: {
    isEven: (state) => state.count % 2 === 0,
  },
  actions: {
    actionIncrement({commit}) {
      commit('increment')
    },
  },
}

説明

namespaced: true,

これを指定して名前空間を分けます。
もしmoduleB.jsに同じ名前のmutationやgetterがあった場合でも、名前空間が違うので被ることはありません。(指定しないと、どちらかの定義が上書きされるのかな??)

state: getDefaultState(),

stateの初期値を返却する関数を作ることでstateのリセットをしやすくしてみました。
Vue.jsではオブジェクト内の一部を変更してもリアクティブに伝搬してくれないので、Object.assign(state, getDefaultState())でstate全体を変更することで対応しています。

ちなみに、getDefaultStateを関数ではなく変数にしてしまうと、コンポーネント側で色々とstateを変えたりしているとgetDefaultStateの値も変わってしまいます。(参照渡しになるので)

これはダメ

const getDefaultState = {
  message: '',
  count: 0,
}

export default {
  namespaced: true,
  state: getDefaultState,
  //・・・・・

actionIncrement({commit}) {

actionは引数にコンテキストオブジェクトを受け取るが、この例ではcommitしか使わないので引数分割の記法を使っています。
以下と同じこと。

    actionIncrement(context) {
      context.commit('increment')
    },

コンテキストオブジェクトには、他にも state getters dispatch があるので利用したいものを複数指定するのもあり。
こんな感じ actionIncrement({state, commit}) {

moduleB.jsファイルを作る

moduleA.jsと同じ感じなので省略。

モジュールファイルをstoreに登録する

index.js
import Vuex from 'vuex'
import moduleA from '@/store/modules/moduleAjs'
import moduleB from '@/store/modules/moduleB.js'

export default new Vuex.Store({
  modules: { moduleA, moduleB },
})

説明

modules: { moduleA, moduleB },

モジュールファイルを登録します。
オブジェクトキー省略記法を使っていますが、下記と同じことです。


  modules: {
     moduleA: moduleA,
     moduleB: moduleB
  },

VuexをVueに登録

あとはindex.jsをインポートしてVueオブジェクトを作るだけ。


Vue.use(Vuex)

new Vue({
  store,
}).$mount('#app')

moduleを利用する

モジュール分割して namespaced: trueにしている前提の呼び出し方です。

VueのComponentで利用する

stateの参照


const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.moduleA.count
    }
  }
}

形式は以下の通り。

store.state.【modulesオブジェクトのキー】.【stateの値】

※modulesオブジェクトは先ほど上で書いたやつです。

mutationを使う


  methods: {
    setMessage(text) {
      this.$store.commit('moduleA/message', text)
    },
  },

形式は以下の通り。

store.commit('【modulesオブジェクトのキー】/【mutation名】', 【mutationの引数】)

mutationの引数が要らない場合は第一引数のみでOK。

その他

action

store.dispatch('【modulesオブジェクトのキー】/【action名】', 【actionの引数】)

getter

store.getters['modulesオブジェクトのキー】/【getter名】']

moduleAの例で言うと this.$store.getters['moduleA/isEven']

他のモジュールファイル内から利用する

今回の例で言うと、moduleB.js内でmoduleA.jsのstateやactionを使いたい場合。

getter内で利用する場合

getterの第三引数と第四引数にルート参照できる変数がそれぞれ渡されるので、そこからアクセスする。

moduleB.js
getters: {
  someGetter(state, getters, rootState, rootGetters) {
    //moduleAのstateを参照する
    const count = rootState.moduleA.count
    //moduleAのgetterを使う
    const isEven = rootGetters['moduleA/isEven'] 
  },
},

action内で利用する場合

stateとgettterは、引数のコンテキストオブジェクト内にルート参照できる変数が入っているので、そこからアクセスする。
actionとmutationは、dispatch commit の引数を増やすことで対応する。

moduleB.js
actions: {
  someAction({ dispatch, commit, rootState, rootGetters }) {
    //moduleAのstateを参照する
    const count = rootState.moduleA.count
    //moduleAのgetterを使う
    const isEven = rootGetters['moduleA/isEven'] 
    //moduleAのactionを使う
    dispatch('moduleA/actionIncrement', null, { root: true })
    //moduleAのmutationを使う
    commit('moduleA/message', 'メッセージだよ', { root: true })
  },
},

dispatchの第二引数はactionには渡したい値を入れます。
第三引数の{ root: true } でstoreのルートから辿る様になり、 第一引数の'moduleA/actionIncrement' でモジュール指定できます。 → APIリファレンス

commitも同様ですね。

ヘルパー関数の場合

ヘルパー関数の場合は第一引数にnamespaceを指定する。 → APIリファレンス

import { mapState } from 'vuex'

export default {
  computed: mapState('moduleA', {
    count: state => `${state.count}回`,
  })
}

参考

Vuex公式(日本語)

11
7
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
11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?