LoginSignup
6
4

More than 3 years have passed since last update.

Vuexのmoduleのstateを呼ぶ時store.modules.◯◯.stateにならない理由

Posted at

Vuexにはモジュールという機能があり、コンポーネントが複雑化して大きな一つのstoreにstateを管理するには難しくなった時のために、モジュールという単位で分けることで管理をすることが出来ます。

以下はmoduleの例です。Vuexのドキュメントに載っているものの写しです。
https://vuex.vuejs.org/ja/guide/modules.html

modules.js
const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> `moduleA` のステート
store.state.b // -> `moduleB` のステート

moduleAとmoduleBはどちらもstateとmutationsを持っており、new Vuex.Storeでインスタンス化したオブジェクトをstore変数に入れています。その時modulesプロパティの中でmoduleAをa、moduleBをbというプロパティにセットしています。

ここで疑問が浮かんだのですが、moduleA内のstateにアクセスする時はstore.state.aとなります。「aの中のstate」を呼んでいるのに「stateの中からaを呼んでいる」ので、直感的にはstore.modules.a.stateではないかと思いました。しかしstore.state.aは間違っていません。

理由を探るためVuexのソースを見てみました。結論から言うと、Storeクラス内にモジュール内のstateとは全く別のstateというオブジェクトを作り、モジュール内のstateを代入して置き換えています。

詳しくVuexのコードを見てみます。上のコードのstore変数はnew Vuex.Storeで作ったオブジェクトなので、Storeクラスの中にstateオブジェクトがありそうです。

Storeクラスはsrc/store.jsに定義されていました。
https://github.com/vuejs/vuex/blob/dev/src/store.js
stateは以下のように定義されていました。関係のある部分だけまとめています。

src/store.js
import ModuleCollection from './module/module-collection'

export class Store {
  constructor(options = {}) {
    this._modules = new ModuleCollection(options)
    const state = this._modules.root.state
  }
}

Storeクラスのstate変数にthis._modules.root.stateが入っています。これが一番上の例でいうstore.stateに該当します。

Storeをインスタンス化する時引数に入ったオブジェクトをoptionsに入れています。このoptionsが一番上の例の{modules: {a: moduleA, b: moduleB}}の部分となります。
optiosを引数に入れたModuleCollectionクラスを初期化して_modulesに入れ、中のroot.stateを取り出しているようです。

ここで注意するべきはstore.jsの中にmodulesという変数はありません。したがってstore.modules.state.aと書いてしまうとstore.modulesがundefinedであるためにエラーになります。

ではstore.stateにセットしているrootは何なのか、moduleAとmoduleBはどのように取り出しているのかさらに掘り下げるためにModuleCollectionクラスを見てみます。こちらも関係ある部分だけを抜粋しました。
https://github.com/vuejs/vuex/blob/dev/src/module/module-collection.js

src/module/module-collection.js

import Module from './module'
import { forEachValue } from '../util'

export default class ModuleCollection {
  constructor (rawRootModule) {
    // register root module (Vuex.Store options)
    this.register([], rawRootModule, false)
  } 

  register (path, rawModule, runtime = true) {
    if (process.env.NODE_ENV !== 'production') {
      assertRawModule(path, rawModule)
    }
    const newModule = new Module(rawModule, runtime)
    if (path.length === 0) {
      this.root = newModule
    } else {
      const parent = this.get(path.slice(0, -1))
      parent.addChild(path[path.length - 1], newModule)
    }

    // register nested modules
    if (rawModule.modules) {
      forEachValue(rawModule.modules, (rawChildModule, key) => {
        this.register(path.concat(key), rawChildModule, runtime)
      })
  }
}

ModuleCollectionをnewするときに引数にnew Vuex.Storeの引数と同じものを入れており、それを使ってregister関数を実行しています。
register関数内でModuleクラスをnewしており、this.rootに代入しています。これが先ほどstore.jsでみたthis._modules.rootとなります。

もしmodulesがあれば下の方のif(rawModule.modules)の中身が通るため、中のオブジェクトループで回して再帰でregister関数を実行しているようです。このrawChildModuleに上の例でいうmoduleAmoduleBが入ってきます。

newしているModuleクラスのコンストラクタもみてみます。
https://github.com/vuejs/vuex/blob/dev/src/module/module.js

src/module/module.js
export default class Module {
  constructor (rawModule, runtime) {
    this.runtime = runtime
    // Store some children item
    this._children = Object.create(null)
    // Store the origin module object which passed by programmer
    this._rawModule = rawModule
    const rawState = rawModule.state

    // Store the origin module's state
    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
  }
}

このrawModuleには上の例でいうmoduleA、moduleBが入るため、その中のstateを新たにthis.stateに代入しているようです。これでstore.stateの中に各モジュール内のstateの中身を移すことが出来ました。

6
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
6
4