Skip to content

ModuleCollection

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时, store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成 模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套 子模块。ModuleCollection 用于将用户的模块配置项转化为树形结构。用户配置:

javascript
const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})
javascript
class ModuleCollection {
  constructor (rawRootModule) {

    // 构造器中调用 register 注册模块,register 接收参数 path 和 rawModule
    // path 数组表示当前模块路径,用于表示模块之间的层级关系。第一次调用时传的 path 
    // 是空数组,代表根模块。
    //   - ['a'] 就是 a 模块
    //   - ['a', 'b'] 就是 a 模块下的 b 模块,
    // rawModule 就是我们写的当前模块的配置项对象,是还没有处理的。
    // 我们通过 Module 类将配置项对象处理成一个新对象 newModule,newModule 中有这些
    // 属性:
    //   - _children,是一个对象,存储着当前模块的子模块,这些子模块也都经过了
    //     Module 类处理
    //   - _rawModule,表示未经处理的当前模块,即 rawModule,我们写的当前模块的配置项
    //   - state,表示当前模块的状态,即 rawModule.state
    // newModule 需要挂载到 parent._children 中,而我们可以通过 path 中的倒数第二项
    // 来找到 parent
    this.register([], rawRootModule)
  }

  register (path, rawModule) {
    const newModule = new Module(rawModule, runtime)
    if (path.length === 0) {

      // 第一次调用 register,将生成的 newModule 挂载到 _modules.root 上,
      // _modules 就是 new ModuleCollection 的实例,_modules 最终会挂载到 store 上。
      this.root = newModule
    } else {

      // 后续调用 register,将生成的 newModule 挂载到 parent._children 上
      //   - 对于 ['a'] 来说父模块就是根模块,即 this.root
      //     a 模块生成的 newModule 会挂载到 this.root._children 上
      //   - 对于['a', 'b']来说父模块就是 a 模块,
      //     b 模块生成的 newModule 会挂载到 this.root._children.a._children 上
      // 因此先 path.slice(0, -1) 取出上级模块,通过 reduce 完成逐级查找
      // 找到 parent 后将 newModule 挂载到 parent._children 中,挂载的子模块名就是
      // path 数组的最后一项。
      const parent = this.get(path.slice(0, -1))
      parent.addChild(path[path.length - 1], newModule)
    }

    // register nested modules
    if (rawModule.modules) {

      // 根模块处理完后遍历 rawModule.modules,递归调用 register 将子模块通过
      // Module 类处理生成 newModule 并挂载到 parent._children 中。
      // 需要通过 path 倒数第二项找到 parent,因此调用 register 时的 path 参数
      // 是当前 path 数组拼接上子模块的名字的结果。
      forEachValue(rawModule.modules, (rawChildModule, key) => {
        this.register(path.concat(key), rawChildModule)
      })
    }
  }

  get (path) {
    return path.reduce((module, key) => {
      return module.getChild(key)
    }, this.root)
  }
}

class Module {
  constructor (rawModule, runtime) {
    this.runtime = runtime
    // 子模块
    this._children = Object.create(null)
    // 用户配置
    this._rawModule = rawModule
    const rawState = rawModule.state

    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
  }

  addChild (key, module) {
    this._children[key] = module
  }
}