2. Module initialization

From Store constructor, a sequence of data initializations is performed, which executes this._modules = new ModuleCollection(options), assuming that the option written is structured like this

const modulesA = {
  namespaced: true.state: {
    conut: 1
  },
  getters: {
    computedConut(state) {
      return state.conut + 1}},mutations: {
    add(state) {
      state.conut + 1}},actions: {
    addCount(context) {
      context.commit('add')}}}const store = new Vuex.Store({
  modules: {
    modulesA
  },
  state: {
    conut: 1
  },
  getters: {
    computedConut(state) {
      return state.conut + 1}},mutations: {
    add(state) {
      state.conut + 1}},actions: {
    addCount(context) {
      context.commit('add')}}})Copy the code

ModuleCollection constructor (rawRootModule) constructor (the previous options) executes this. Register, passing an empty array as the first argument. The second argument is passed to rawRootModule and the third argument is passed false. // Register root Module (vuex.store options) Register first passes const newModule = newModule (rawModule, Runtime), rawModule is rawRootModule, Runtime is false. Module constructor saves the rawModule passed to it by executing this._rawModule = rawModule. Const rawState = rawModule.state to save the state of the rawModule as the root of the object passed in, This. state = (typeof rawState === ‘function’? RawState () : rawState) | | {} to save the state, that is the root of the state can be the form of objects, also can be in the form of a function to return object. The newModule of the register function is then an instance of Module. The register function will then determine the length of path, which will pass in an empty array for the first time, so this. Root = newModule will be called and our instantiated newModule will be root. Modules: RawModule. modules: rawModule.modules: rawModule.modules: This. Register (path.concat(key), rawChildModule, Runtime). Path is an empty array at first. This makes path [‘modulesA’], and the second argument is the value of modulesA. Const parent = this.get(path.slice(0, -1)) const parent = this.get(path.slice(0, -1)) So we pass in an empty array. The get function does a reduce on the passed path. Each reduce execution passes the current key as an argument to the Module instance’s getChild method, which executes and returns the result. For get, the path argument passed in is an empty array, so this.root is returned directly. Parent is the Module instance of register that was first executed. Parent-addchild (path[path.length-1], newModule) is passed as the first parameter to the last bit of path, which is moduleA string. The second argument is passed to the register Module instance, and addChild will call this._children[key] = Module, thus storing the Module instance created by ModuleA in the root Module instance _children. When modulesA’s modules execute to get, the modulesA module instance is held as parent and the appendChild operation continues, recursively establishing the parent-child relationship between module instances.

// src/module/module-collection.js
class ModuleCollection {
  constructor (rawRootModule) {
    // register root module (Vuex.Store options)
    this.register([], rawRootModule, false)
  }
  get (path) {
    return path.reduce((module, key) = > {
      return module.getChild(key)
    }, this.root)
  }
  ...
  register (path, rawModule, runtime = true) {...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)
      })
    }
  }
  ...
}
// 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) || {}
  }
  ...
  getChild (key) {
    return this._children[key]
  }
  ...
}
Copy the code

Three, module installation

Executing new ModuleCollection(options) will mount _modules for the store instance, and then execute installModule. Add: // init root module.,// This also recursively enables all sub-modules and // And collectworks all module getters Inside this._wrappedgetters, this means registering root modules and recursively installing all submodules, and collecting getters for all modules into this._wrappedgetters.

1. Execute makeLocalContext to get local

When installModule is executed, the first argument is passed to the current store instance, the second argument to the root state, the third argument to the empty array, and the fourth argument to the root module instance. The installModule function first executes const isRoot =! Const namespace = store._modules. GetNamespace (path), The getNamespace function gets their Module based on the passed path array, and returns key+/ if namespaced is true, if the passed path is [‘modulesA’,’modulesB’], And if namespaced is true for both, modulesA/modulesB/ is returned, and the path passed in for the first execution is an empty array, so the namespace is an empty string for the first time. If the fourth argument, modulenamespaced, is true, then store._modulesNamespacemap [namespace] is executed. That is, the Store. _modulesNamespaceMap object stores module instances where namespaced is true, with namespace as the key. Const local = module.context = makeLocalContext(store, namespace, path), makeLocalContext There is dispatch and commit, followed by the return result of accessing getters and state via Object.defineProperties for Local, which eventually returns Local. Both dispatch and commit check for Namespace, and if it’s not an empty string, the module’s namespaced is false, it will register directly. If it has a Namespace, it will execute type = Namespace + type. That is registered in vuex with namesped mutations in the module and the actions of the access method that eventually will be modulesA/modulesB/the methods, So makeLocalContext overrides commit and dispatch for us. If there is one, makeLocalGetters will be executed. MakeLocalGetters will be executed to determine store._makeLocalGettersCache[Namespace]. If the module does not have a getter cache for the current module, getters will be iterated through to get the last getter name for the key, and object.defineProperty will bind the current getter to the global getter. This allows global objects such as modulesA/modulesB/ XXXX to access the current Module’s getter. State access different from before, he is by the state. The ModulesA. ModulesB. XXX, corresponding getNestedState function, Return path.reduce((state, key) => State [key], state), so that when accessing the state of the submodule, it will search for the state of the submodule and finally return the state of the access module. This way, when configuring the Store, we don’t have to worry about its nesting hierarchy.

// src/store.js
function installModule (store, rootState, path, module, hot) {
  constisRoot = ! path.lengthconst namespace = store._modules.getNamespace(path)
  // register in namespace map
  if (module.namespaced) {
    ...
    store._modulesNamespaceMap[namespace] = module}...const local = module.context = makeLocalContext(store, namespace, path)
  ...
}

/** * make localized dispatch, commit, getters and state * if there is no namespace, just use root ones */
function makeLocalContext (store, namespace, path) {
  const noNamespace = namespace === ' '

  const local = {
    dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) = >{...if(! options || ! options.root) { type = namespace + type ... }return store.dispatch(type, payload)
    },
    commit: noNamespace ? store.commit : (_type, _payload, _options) = >{...if(! options || ! options.root) { type = namespace + type ... } store.commit(type, payload, options) } }// getters and state object must be gotten lazily
  // because they will be changed by vm update
  Object.defineProperties(local, {
    getters: {
      get: noNamespace
        ? () = > store.getters
        : () = > makeLocalGetters(store, namespace)
    },
    state: {
      get: () = > getNestedState(store.state, path)
    }
  })

  return local
}

function makeLocalGetters (store, namespace) {
  if(! store._makeLocalGettersCache[namespace]) {const gettersProxy = {}
    const splitPos = namespace.length
    Object.keys(store.getters).forEach(type= > {
      // skip if the target getter is not match this namespace
      if (type.slice(0, splitPos) ! == namespace)return

      // extract local getter type
      const localType = type.slice(splitPos)

      // Add a port to the getters proxy.
      // Define as getter property because
      // we do not want to evaluate the getters in this time.
      Object.defineProperty(gettersProxy, localType, {
        get: () = > store.getters[type],
        enumerable: true
      })
    })
    store._makeLocalGettersCache[namespace] = gettersProxy
  }

  return store._makeLocalGettersCache[namespace]
}
Copy the code