Skip to content

3Store实例化

初始化模块

由于使用单一状态树,应用所有状态会集中到一个比较大对象,当应用变复杂时,store对象可能很臃肿。为解决该问题,Vuex允许我们将store拆成多个module,每个module可拥有自己的state、mutation、action、getter,甚至是嵌套子模块

从数据结构上看,模块的设计就是一个树形结构,store本身可以理解为一个root module,它下面的modules就是子模块,Vuex需要完成这棵树的构建,其构建入口就是

this._modules = new ModuleCollection(options)

ModuleCollection定义在vuex/src/module/module-collection.js

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

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

  getNamespace(path) {
    let module = this.root
    return path.reduce((namespace, key) => {
      module = module.getChild(key)
      return namespace + (module.namespaced ? key + '/' : '')
    })
  }

  update(rawRootModule) {
    update([], this.root, rawRootModule)
  }

  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)
      })
    }
  }

  unregister(path) {
    const parent = this.get(path.slice(0, -1))
    const key = path[path.length - 1]
    if (!parent.getChild(key).runtime) return

    parent.removeChild(key)
  }
}

ModuleCollection实例化过程就是执行了register方法,register接收3个参数,其中path表示路径,因为我们整体目标是要构建一棵模块树,path是在构建树的过程中维护路径; rawModule表示定义模块的原始配置; runtime表示是否是一个运行时创建的模块

register方法首先通过const newModule = new Module(rawModule, runtime)创建了一个Module的实例,Module是用来描述单个模块的类,它的定义在vuex/src/module/module.js

js
export default 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) || {}
  }

  get namespaced() {
    return !!this._rawModule.namespaced
  }

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

  removeChild(key) {
    delete this._children[key]
  }

  getChild(key) {
    return this._children[key]
  }

  update(rawModule) {
    this._rawModule.namespaced = rawModule.namespaced
    if (rawModule.actions) {
      this._rawModule.actions = rawModule.actions
    }
    if (rawModule.mutations) {
      this._rawModule.mutations = rawModule.mutations
    }
    if (rawModule.getters) {
      this._rawModule.getters = rawModule.getters
    }
  }

  forEachChild (fn) {
    forEachValue(this._children, fn)
  }

  forEachGetter (fn) {
    if (this._rawModule.getters) {
      forEachValue(this._rawModule.getters, fn)
    }
  }

  forEachAction (fn) {
    if (this._rawModule.actions) {
      forEachValue(this._rawModule.actions, fn)
    }
  }

  forEachMutation (fn) {
    if (this._rawModule.mutations) {
      forEachValue(this._rawModule.mutations, fn)
    }
  }
}

来看一下 Module 的构造函数,对于每个模块而言,this._rawModule 表示模块的配置,this._children 表示它的所有子模块,this.state 表示这个模块定义的 state。

回到 register,那么在实例化一个 Module 后,判断当前的 path 的长度如果为 0,则说明它是一个根模块,所以把 newModule 赋值给了 this.root,否则就需要建立父子关系了:

共 20 个模块,1301 篇 Markdown 文档。