preface

The Store centralizes the state of the application, but if the application becomes very complex, with many states, the store can become quite bloated. The Module helps the Store divide into modules, each with its own state, getter, mutation, Action, and module.

How are modules divided and how do they manage their state? Let’s look at the implementation of the Module.

To prepare

Before reading, you need to know the following:

  1. Array.prototype.reduce()
  2. Vue.set()

Interpretation of the

By default, actions, mutations, and getters inside modules are registered in the global namespace — allowing multiple modules to respond to the same mutation or action.

What does that mean? Take a look at the following example:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    addNote () {
      console.log('root addNote')}},modules: {
    a: {
      state: {
        count: 0
      },
      mutations: {
        addNote () {
          console.log('module a addNote')}}}}})Copy the code

After module is used, state is modularized. For example, to call the state of the root module, call store.state.count, or to call the state of the A module, call store.state.a.ount.

However, the mutation in the example is registered globally, that is, a call to store.mit (‘addNote’) will call the mutation of the following module and a module. Unless the module names are distinguished, the call will be triggered as soon as the same name is committed.

Of course, later versions of Vuex 2.0.0 add namespaces to make Modules more modular.

So in the module to be read, the action, mutation, and getter are still under the global module as long as state is modularized.

The modules registered

Module registration is implemented in installModule. Navigate to installModule methods.

function installModule (store, rootState, path, module, hot) {
  constisRoot = ! path.lengthconst {
    modules
  } = module

  // set state
  if(! isRoot && ! hot) {const parentState = getNestedState(rootState, path.slice(0.- 1))
    const moduleName = path[path.length - 1]
    store._withCommit((a)= > {
      Vue.set(parentState, moduleName, state || {})
    })
  }
  
  // mutation registration
  // Action registration
  // Register the getter

  if (modules) {
    Object.keys(modules).forEach(key= > {
      installModule(store, rootState, path.concat(key), modules[key], hot)
    })
  }
}
Copy the code

If you look at the simplified code, you can see that installModule initializes modules in two steps. The first step is to use vue.set () to set a listener on the state of the current module. The second step is to continue traversing the submodules and recursively call installModule.

set state

So the core implementation of Modules is to listen on the current module state and extract this code:

const parentState = getNestedState(rootState, path.slice(0.- 1))
const moduleName = path[path.length - 1]
store._withCommit((a)= > {
  Vue.set(parentState, moduleName, state || {})
})
Copy the code

First guess the getNestedState method to get the parent state. So get the parent state first, then get the current module name, and finally use vue.set () to set the current state to the parent state. In effect, this implementation adds attributes to data.state in a Vue instance and enables the VUE instance to listen for changes to the added attributes.

getNestedState

const parentState = getNestedState(rootState, path.slice(0.- 1))
Copy the code

Slice (0, -1) removes the current module and passes it as parameter and rootState to getNestedState, which returns the parentState of the current module, parentState.

Let’s look at the implementation of getNestedState:

function getNestedState (state, path) {
  return path.length
    ? path.reduce((state, key) = > state[key], state)
    : state
}
Copy the code

If length is equal to 0, that is, only the root state is returned. Alternatively, if there are nested modules, the array.prototype.reduce () method goes all the way to the root state property and returns the state corresponding to path.

At this point, the modularity of State is registered, and a recursive call to installModule completes the registration of all modules.

Since we are adding attributes to rootState, we can obtain the module through store.state.a and then continue to obtain the state in the module.

get modules state

The getNestedState method was omitted from mutation and action. When registering mutation and action, the following code appears:

getNestedState(store.state, path)
Copy the code

This code actually gets the state of the current modules and passes it back as an argument.

Storage array

Mutation is stored in the Store. _mutations array. The main goal is to store all modules’ mutations in an array so that all mutations can be triggered at commit time.

Getters and actions use arrays for the same reason.

However, if two modules have the same mutation name, vuEX 2.0.0 cannot trigger only one mutation. This can be done in later versions by setting the namespace.

conclusion

This is an interpretation of module. Registration Module is not as complex as expected, mainly divided into two steps.

The first step is to find the parent state of the current Module and bind at least the current state listener to it to ensure that changing the state triggers the corresponding change.

The second step is to recurse the module, ensuring that the state of the child module is set so that the child module can be nested.