1, the preface

See that vuex in the corporate framework uses the vuex-module-decorators extension module. Call vuEX’s State, Getter, Mutation, and Action using object style calls. Read the source code for vuex-module-decorators and write an article about how vuex-module-decorators are used and what you think is wrong.

This article will show you how to use vuex-module-decorators so that you can use vuex more smoothly in your development process.

Let’s take a look at the code for vuex-module-decorators and see how it works. Let everyone avoid some possible problems in the process of use, and when encountering problems can be more convenient to locate and solve the problem.

Hopefully, reading this article will give you a better understanding of Vuex and vuex-module-decorators.

2 What are vuex-module-decorators?

Decorators vuex-module-decorators is an extension of Vuex that allows you to manipulate Vuex as if you were calling an object, providing type-safe type validation and auto-complete/alert/quick location methods in the IDE. The website address

4 vuex – module – decorators

The vuex-module-decorators module rewraps the State, Getter, Mutation, Action, namespace, and dynamic modules on top of the original vuEX structure. Write vuex as if it were a class when writing code. Use vuex as an object when you use it.

4.1 Vuex-module-decorators vs. Vuex definition styles
import { VuexModule, Module, Mutation, Action } from 'vuex-module-decorators'
import { get } from 'axios'

interface PostEntity {
  comments: string[]
}

@Module
export default class Posts extends VuexModule {
  posts: PostEntity[] = [] // initialize empty for now

  get totalComments() :number {
    return this.posts
      .filter(post= > {
        // Take those posts that have comments
        return post.comments && post.comments.length
      })
      .reduce((sum, post) = > {
        // Sum all the lengths of comments arrays
        return sum + post.comments.length
      }, 0)
  }

  @Mutation
  updatePosts(posts: PostEntity[]) {
    this.posts = posts
  }

  @Action({ commit: 'updatePosts' })
  async fetchPosts() {
    return get('https://jsonplaceholder.typicode.com/posts')}}Copy the code

Convert to Vuex style code

module.exports = {
  state: {
    posts: []},getters: {
    totalComments: (state) = > {
      return state.posts
        .filter((post) = > {
          return post.comments && post.comments.length
        })
        .reduce((sum, post) = > {
          return sum + post.comments.length
        }, 0)}},mutations: {
    updatePosts: (state, posts) = > {
      // 'posts' is payload
      state.posts = posts
    }
  },
  actions: {
    fetchPosts: async (context) => {
      // the return of the function is passed as payload
      const payload = await get('https://jsonplaceholder.typicode.com/posts')
      // the value of 'commit' in decorator is the mutation used
      context.commit('updatePosts', payload)
    }
  }
}
Copy the code
Comparison of Call modes
Access to the state

Vuex – module – decorators calls

UserModule.userCode
Copy the code

Vuex call

store.state[name?] .userCodeCopy the code
To obtain the getter

Vuex – module – decorators calls

SappModule.currentTab
Copy the code

Vuex call

store.getters.[name/?] currentTabCopy the code
Call the Mutation

Vuex – module – decorators calls

SappModule.SET_JDEID(eid)
Copy the code

Vuex call

store.commit([name/?]SET_JDEID, eid)
Copy the code
Call to Action

Vuex – module – decorators calls

SappModule.getMenus()
Copy the code

Vuex call

store.dispatch([name/?] getMenus)Copy the code
4.2 Vuex State defined in vuex-module-decorators

All attributes in vuex-module-decorators defined classes are converted to the VUex state.

Official State document

import { Module, VuexModule } from 'vuex-module-decorators'

@Module
export default class Vehicle extends VuexModule {
  wheels = 2
}
Copy the code

Is equivalent to:

export default {
  state: {
    wheels: 2}}Copy the code
4.3 Vuex-module-decorators Define the getters for vuex

All ES6 getters in vuex-module-decorators defined classes are converted to vuex getters.

Official Getter document

import { Module, VuexModule } from 'vuex-module-decorators'

@Module
export default class Vehicle extends VuexModule {
  wheels = 2
  get axles() {
    return this.wheels / 2}}Copy the code

Is equivalent to:

export default {
  state: {
    wheels: 2
  },
  getters: {
    axles: (state) = > state.wheels / 2}}Copy the code

Getter definitions with parameters (method-style Access) :

@Module
export default class Vehicle extends VuexModule {
  companies = []
  get company() {
    return (companyName: string) = > { this.companies.find(company= >company.name === companyName) }; }}Copy the code
4.4 Vuex Mutation defined in vuex-module-decorators

All methods modified with the modified function @mutation are converted to Vuex Mutation. Official Mutation document

import { Module, VuexModule, Mutation } from 'vuex-module-decorators'

@Module
export default class Vehicle extends VuexModule {
  wheels = 2

  @Mutation
  puncture(n: number) {
    this.wheels = this.wheels - n
  }
}
Copy the code

Is equivalent to:

export default {
  state: {
    wheels: 2
  },
  mutations: {
    puncture: (state, payload) = > {
      state.wheels = state.wheels - payload
    }
  }
}
Copy the code

The Mutations method runs on the @mutation decorator, and sets this to state, so you want to change the state in state: state.item++. It’s easy this.item++

4.5 Vuex-module-decorators Define vuex actions

All decorated functions @action modifier methods are converted to vuex’s official Action document

import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'
import { get } from 'request'

@Module
export default class Vehicle extends VuexModule {
  wheels = 2

  @Mutation
  addWheel(n: number) {
    this.wheels = this.wheels + n
  }

  @Action
  async fetchNewWheels(wheelStore: string) {
    const wheels = await get(wheelStore)
    this.context.commit('addWheel', wheels)
  }
}
Copy the code

Is equivalent to:

const request = require('request')
export default {
  state: {
    wheels: 2
  },
  mutations: {
    addWheel: (state, payload) = > {
      state.wheels = state.wheels + payload
    }
  },
  actions: {
    fetchNewWheels: async (context, payload) => {
      const wheels = await request.get(payload)
      context.commit('addWheel', wheels)
    }
  }
}
Copy the code

Methods decorated with the @action function will be called this at run time with the following shape -{… [All fields of state], context}. Therefore, to manually commit mutation from within the body of the action, simply call this.context.com MIT (‘mutationName’, mutPayload). PS: This will be introduced later when introducing the source code

4.6 vuex-module-decorators: MutationActions

In addition to the state, getter, mutation, and Action described above, the vuex-module-decorators module supports another decorator, MutationActions, defined as MutationActions. The MutationActions decorator goes through the following process

1. Invoke an action to execute the asynchronous script. 2. Next, look at the value returned from the action. Commit the result value to the store.

Vuex – module – decorators

import {VuexModule, Module, MutationAction} from 'vuex-module-decorators' 

@Module
class TypicodeModule extends VuexModule {
  posts: Post[] = []
  users: User[] = [] 

  @MutationAction 
  async function updatePosts() {
    const posts = await axios.get('https://jsonplaceholder.typicode.com/posts')

    return { posts }
  }
}
Copy the code

Is equivalent to:

const typicodeModule = {
  state: {
    posts: [].users: []},mutations: {
    updatePosts: function (state, posts) {
      state.posts = posts
    }
  },
  actions: {
    updatePosts: async function (context) {
      const posts = await axios.get('https://jsonplaceholder.typicode.com/posts')
      context.commit('updatePosts', posts)
    }
  }
}
Copy the code
4.7 Vuex-module-decorators define vuex namespace (Namespaced Modules)

Specify namespace (Namespaced Modules) in the Class decorator @Module with {namespaced: true}

@Module({ namespaced: true.name: 'mm' })
class MyModule extends VuexModule {
  wheels = 2

  @Mutation
  incrWheels(extra: number) {
    this.wheels += extra
  }

  @Action({ root: true.commit: 'setWheels' })
  clear() {
    return 0
  }

  get axles() {
    return this.wheels / 2}}const store = new Vuex.Store({
  modules: {
    mm: MyModule
  }
})
Copy the code

The Name field in the decorator should match the actual name that you will assign to the module when you create a Store.

{root: true} So that dispatch(‘mm/clear’) does not need to be used when the clear method is called through dispatch in the namespace module

4.8 Vuex-module-decorators define vuex Dynamic Modules

Vuex-module-decorators can simply pass some attributes to the @Module decorator to a class {dynamic: true, store, name: An important part of the process is that we must first create the store and then pass the store to the module. Dynamic Modules

Step 1: Create the store

// @/store/index.ts
import Vuex from 'vuex'

const store = new Vuex.Store({
  /* Ideally if all your modules are dynamic then your store is registered initially as a completely empty object */
})
Copy the code

Step 2: Create a dynamic module

// @/store/modules/MyModule.ts
import store from '@/store'
import {Module, VuexModule} from 'vuex-module-decorators'

@Module({dynamic: true, store, name: 'mm'})
export default class MyModule extends VuexModule {
  /* Your module definition as usual */
}
Copy the code

5 Vuex-module-decorators

5.1 Vuex-module-decorators source code structure
moduleFolder: @module decorator processing action file: @action decorator processing config file: some common configuration (rawError: rawError) helper file: some pr extension method index file: entry file, which will export vuex-module- Decorators provide some decorators (Module, Action, Mutation, MutationAction), some base type definitions (VuexModule), some extension methods (getModule), common definitions (config) Moduleoptions file: TS interface definition file mutation file: @mutation decorator processing procedure mutationAction file: @mutationAction decorator processing procedure vuexModule file: GetModule method processCopy the code
5.2 vuex – module – decorators principle

By handling properties defined in a class, getters, methods modified by the @Action modifier, methods modified by the @mutation modifier, methods modified by the @mutationAction modifier, classes modified by the @Module modifier, Vuex’s state, getter, Action, mutation structures wrapped in vuex and dynamically registered as vuex with vuex.store () or registerModule are named objects (passing the name attribute in the @Module modifier). In the object returned via getModule, you can return the state of vuex by accessing the value/method of the class/call vuex action and mutation using COMMIT, Dispatch

5.3 VuEX Configuration
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')}}})Copy the code
5.4 action. Ts file

The @Action decorator wraps the Action structure of the class method when vuex is called and saves it in the class’s Actions

/* * Action decorator that mounts the method to the actions property of the class * @param params * @returns */
function actionDecoratorFactory(params) {
  const { commit = undefined, rawError = !! config.rawError, root =false } = params || {}
  return function(target, key, descriptor) {
    Get the constructor of the current class
    const module = target.constructor
    // Check whether the actions attribute is defined in the current constructor
    if (!module.hasOwnProperty('actions')) {
      // Collect the objects defined in the actions attribute.
      module.actions = Object.assign({}, module.actions)
    }
    // Get the @action modifier modifier
    const actionFunction = descriptor.value
    / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    // Create a new method and mount it to the action. This method is called when the action is executed directly in vuex, taking the vuex context as an argument
    const action = async function(context, payload) {}
    / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    // If it is the root node, there is a method containing root and handler, otherwise there is only one method
    module.actions[key] = root ? { root, handler: action } : action
  }
}
Copy the code
/**
 * The @Action decorator turns an async function into an Vuex action
 *
 * @param targetOrParams the module class
 * @param key name of the action
 * @param descriptor the action function descriptor
 * @constructor* /
export function Action(targetOrParams, key, descriptor) {
  if(! key && ! descriptor) {return actionDecoratorFactory(targetOrParams)
  } else {
    actionDecoratorFactory()(targetOrParams, key, descriptor)
  }
}
Copy the code

Action method encapsulation

When the vuex action method is triggered, it calls the methods of the vuex-module-decorators wrapped class and binds the Vuex context to this of the Module class. ModuleAccessor /thisObj object), which can be accessed via this.context

const action = async function(context, payload) {
      try {
        let actionPayload = null

        // Whether the processed object has the _genStatic attribute
        if (module._genStatic) {
          const moduleName = getModuleName(module)
          // Check whether it is the value above rootGetters, otherwise. Retrieve Module
          const moduleAccessor = context.rootGetters[moduleName] ? context.rootGetters[moduleName] : getModule(module)
          // Mount the context to moduleAccessor, which is the value obtained by getModule
          moduleAccessor.context = context
          // Get the value of method return. The object in this method is context
          actionPayload = await actionFunction.call(moduleAccessor, payload)
        } else {
          const thisObj = { context }
          addPropertiesToObject(thisObj, context.state)
          addPropertiesToObject(thisObj, context.getters)
          actionPayload = await actionFunction.call(thisObj, payload)
        }
        // Whether there is a commit. If there is a commit, perform the mutation configured with the value of action
        if (commit) {
          context.commit(commit, actionPayload)
        }
        // Return the result
        return actionPayload
      } catch (e) {}
    }
Copy the code

_genStatic: If the decorator @module defines the name attribute, the _genStatic variable will be generated. If _genStatic exists, this in actionFunction will refer to the value obtained by the getModule +context. If _genStatic is not present, this in actionFunction contains the value of state, getters, and context. Calling @mutation modified methods directly from @action modified methods is not supported at this time

Screenshot of the Actions structure at run time

5.5 mutation. Ts file

The Mutation structure of the current method called vuEX is wrapped using the @mutation decorator and saved to mutations of the class

Mutations decorator, which attaches the method to the mutations property of the class. Mutations directly take the value of mutations * when registering VUEX@param target
 * @param key
 * @param descriptor* /
export function Mutation(target, key, descriptor) {
  Get the constructor of the current class
  const module = target.constructor
  // Determine whether the mutations attribute is defined in the current constructor
  if (!module.hasOwnProperty('mutations')) {
    Gather the objects defined in the mutations attribute (support the writing method of directly defining mutations attributes in the class)
    module.mutations = Object.assign({}, module.mutations)
  }
  // Get the method modified by the @mutation modifier
  const mutationFunction = descriptor.value
  // Mount the method to the class's Mutations property, and execute the method with state as the first entry
  const mutation = function(state, payload) {
    // This points to state when executed by mutationFunction
    mutationFunction.call(state, payload)
  }
  moduleMutations [key] = mutation} mutationFunction. Call (state, payload), mutations functionthisState pointing to vuex, so mutation can only passthisAccess to a property defined, not accessiblemoduleClassconst mutationFunction = descriptor.value
// Mount the method to the class's Mutations property, and execute the method with state as the first entry
const mutation = function(state, payload) {
  mutationFunction.call(state, payload)
}
Copy the code

A screenshot of the mutations structure obtained at runtime

5.6 Module Folder

Package the attributes of the class decorated with @Module into state format, package get into getters format, package the methods decorated with the @mutation decorator into mutations format, and package the methods decorated with the @Action decorator into actions format Vuex dynamic modules registered with registerModule can access vuex objects as directly as objects through _genStatic assembly

/** * module decorator *@param {*} modOrOpt
 * @returns* /
export function Module(modOrOpt) {
  if (typeof modOrOpt === 'function') {
    /* * @Module decorator called without options (directly on the class definition) */
    moduleDecoratorFactory({})(modOrOpt)
  } else {
    /* * @Module({... }) decorator called with options */
    returnModuleDecoratorFactory (modOrOpt)}} moduleDecoratorFactory: Class handling modified by the @Module decorator/** * Class module decorator *@param moduleOptions
 * @returns* /
function moduleDecoratorFactory(moduleOptions) {
  return function(constructor) {
    const module = constructor
    // stateMethods / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *const stateFactory = () = >sf(module) / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / collectionmoduleAssigns the state tostate
    if (!module.state) {
      module.state = moduleOptions && moduleOptions.stateFactory ? stateFactory : stateFactory()
    }
    // Collect all getters
    if (!module.getters) {
      module.getters = {}
    }
    // Namespace
    if (!module.namespaced) {
      module.namespaced = moduleOptions && moduleOptions.namespaced
    }

    let parentModule = Object.getPrototypeOf(module)
    // Whether there is an inheritance from other classes
    while(parentModule.name ! = ='VuexModule'&& parentModule.name ! = =' ') {
      // Copy the getter from parentModule to the module
      addGettersToModule(module, parentModule)
      parentModule = Object.getPrototypeOf(parentModule)
    }
    / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    // copy the methods identified by the class's own get to getters
    addGettersToModule(module.module)
    / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    const modOpt = moduleOptions
    if (modOpt.name) {
      / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      // Define the _genStatic variable for the object to hold the data, which will be retrieved in the getModule
      Object.defineProperty(constructor, '_genStatic'{})/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

      // Add the name attribute to the object, which will be used later by getModuleName
      Object.defineProperty(constructor, '_vmdModuleName'{value: modOpt.name
      })
    }

    if (modOpt.dynamic) {
      / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      // Register the vue asynchronous store module
      registerDynamicModule(module, modOpt)
      / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    }
    return constructor}}Copy the code

Screenshot of state and getters structures at run time

Sf => stateFactory copies the attributes in the Module class to encapsulate the state structure of vuEX

Reserved keywords
const reservedKeys = ['actions'.'getters'.'mutations'.'modules'.'state'.'namespaced'.'commit']
// Collect all states
export function stateFactory(module) {
  // Get the entire attribute list
  const state = new module.prototype.constructor({})
  const s = {}
  Object.keys(state).forEach((key) = > {
    // Filter reserved keywords
    if(reservedKeys.indexOf(key) ! = = -1) {
      if (typeofstate[key] ! = ='undefined') {
        throw new Error(
          `ERR_RESERVED_STATE_KEY_USED: You cannot use the following ['actions', 'getters', 'mutations', 'modules', 'state', 'namespaced', 'commit'] as fields in your module. These are reserved as they have special purpose in Vuex`)}return
    }
    // Check if there is such a value, and it is not function, and collect it in s
    if (state.hasOwnProperty(key)) {
      if (typeofstate[key] ! = ='function') {
        s[key] = state[key]
      }
    }
  })

  return s
}
Copy the code

The addGettersToModule copies the get/getters decorator methods in the Module class into the vuex getters structure to get the list of property names in srcModule’s Prototype Check whether there are get/getters methods encapsulating get/getters methods into vuex’s getters structure

/** * copy the srcModule object getters to targetModule *@param targetModule
 * @param srcModule* /
function addGettersToModule(targetModule, srcModule) {
  Object.getOwnPropertyNames(srcModule.prototype).forEach((funcName) = > {
    // Get the specified attribute information
    // configurable: true
    // enumerable: false
    / / value: ƒ Sapp ()
    // writable: true
    const descriptor = Object.getOwnPropertyDescriptor(
      srcModule.prototype,
      funcName
    )
    // Determine if the current object has a get method, and if so, add it to getters in the targetModule
    if (descriptor.get && targetModule.getters) {
      // Encapsulates the getter structure for vuex
      targetModule.getters[funcName] = function(state, getters, rootState, rootGetters) {
        const thisObj = { context: { state, getters, rootState, rootGetters } }
        addPropertiesToObject(thisObj, state)
        addPropertiesToObject(thisObj, getters)
        // This in the get method contains the value of state in getters, context
        const got = (descriptor.get).call(thisObj)
        return got
      }
    }
  })
}
Copy the code

registerDynamicModule

After state, getters, @Action and @mutation processing, the module structure of VUEX was obtained, and components were registered into VUEX asynchronously

/** * Register asynchronous components *@param module
 * @param modOpt* /
function registerDynamicModule(module, modOpt) {
  if(! modOpt.name) {throw new Error('Name of module not provided in decorator options')}if(! modOpt.store) {throw new Error('Store not provided in decorator options when using dynamic option')
  }

  modOpt.store.registerModule(
    modOpt.name, // TODO: Handle nested modules too in future
    module,
    { preserveState: modOpt.preserveState || false } // Whether to keep the previous state)}Copy the code

A screenshot of the object returned after processing by @Module

5.6 vuexmodule. Ts

The getModule method returns state, getters, action, and mutation that can be accessed in the same way as object properties/methods

import { getModuleName } from './helpers'

/** * get module object, can call method directly, trigger vuex call *@param moduleClass
 * @param store
 * @returns* /
export function getModule(moduleClass, store) {
  Get the name of the current class
  const moduleName = getModuleName(moduleClass)
  // if there is a method with the same name on getters
  if (store && store.getters[moduleName]) {
    return store.getters[moduleName]
  } else if (moduleClass._statics) { // Whether it is statically bound, whether it has been bound, for caching, to prevent reset execution
    return moduleClass._statics
  }

  / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  // Get the variable added to the object in moduleDecoratorFactory, which returns a method containing the store
  const genStatic = moduleClass._genStatic
  / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  if(! genStatic) {throw new Error(`ERR_GET_MODULE_NO_STATICS : Could not get module accessor. Make sure your module has name, we can't make accessors for unnamed modules i.e. @Module({ name: 'something' })`)}Vuex state, getters, mutation, Action, store objects are stored in storeModule.
  // Attributes in storeModule get is hijacked to point to vuex's state and getters when accessed. Methods are pointed to mutation and action of the VUEX
  const storeModule = genStatic(store)

  if (store) {
    store.getters[moduleName] = storeModule
  } else {
    // Store it in the _statics property of the class for future use
    moduleClass._statics = storeModule
  }

  return storeModule
}
Copy the code

5.8 Object Screenshot obtained by the getModule method

5.9 _genStatic method

Concatenate stores to Statics using staticStateGenerator, mount vuex's state to statics object properties using staticGetterGenerator. Mount Vuex's getters to the properties of statics objects using the staticMutationGenerator method, mount Vuex's Mutations to the properties of Statics objects using the staticActionGenerators method, Mount Vuex's Actions to the properties of the Statics object and return an object with a store, vuex's state property list, Vuex's getter property list, Vuex's Mutations method list, and Vuex's Actions method list// Define the _genStatic variable for the object to hold the data, which will be retrieved in the getModule
      Object.defineProperty(constructor, '_genStatic'{value: (store) = > {
          // If a store is passed in, use store directly, otherwise use the store fetched by the Module decorator
          const statics = { store: store || modOpt.store }
          if(! statics.store) {throw new Error(`ERR_STORE_NOT_PROVIDED: To use getModule(), either the module should be decorated with store in decorator, i.e. @Module({store: store}) or store should be passed when calling getModule(), i.e. getModule(MyModule, this.$store)`)}// =========== For statics ==============
          // ------ state -------, mount module state to statics
          staticStateGenerator(module, modOpt, statics)
          / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

          // ------- getters -------
          if (module.getters) {
            // Mount module getters to statics, where sappModule. XXX can fetch the value of statics.store.getters
            staticGetterGenerator(module, modOpt, statics)
            / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
          }

          // -------- mutations --------
          if (module.mutations) {
            Sappmodule.xxx () can be used to call statics.store.mit
            staticMutationGenerator(module, modOpt, statics)
            / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
          }
          // -------- actions ---------
          if (module.actions) {
            // Mount module actions to statics, where sappmodule.xxx () can call statics.store.dispatch
            staticActionGenerators(module, modOpt, statics)
            / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
          }
          return statics
        }
      })
Copy the code

_genStatic Returns the structure screenshot

5.10 staticStateGenerator method

State can be either a method or an object, and objects are generally used today

When a property in statics is accessed, look for the value of the property in store state. If name exists, get the state of the corresponding object

/** * Get module state mounted to statics *@param module
 * @param modOpt
 * @param statics* /
export function staticStateGenerator(module, modOpt, statics) {
  // Get the state of the current module
  const state = modOpt.stateFactory ? module.state() : module.state
  Object.keys(state).forEach((key) = > {
    if (state.hasOwnProperty(key)) {
      // If not undefined or function means it is a state value
      if (['undefined'.'function'].indexOf(typeof state[key]) === -1) {
        // Delegate the object's properties to vuex's state
        Object.defineProperty(statics, key, {
          get() {
            const path = modOpt.name.split('/')
            let data = statics.store.state
            for (const segment of path) {
              data = data[segment]
            }
            return data[key]
          }
        })
      }
    }
  })
}
Copy the code

StaticStateGenerator Execution result

5.11 staticGetterGenerator method

When a property in Statics is called, if you’re looking for store getters, look for the value of that property in store getters, and if you have namespaced, look for name/key.

/** * Mount module getters to statics. Sappmodule. XXX can fetch the value * from statics@param module
 * @param modOpt
 * @param statics* /
export function staticGetterGenerator(module, modOpt, statics) {
  // Mount statics' keys to store getters
  Object.keys(module.getters).forEach((key) = > {
    if (module.namespaced) {
      Object.defineProperty(statics, key, {
        get() {
          return statics.store.getters[`${modOpt.name}/${key}`]}})}else {
      Object.defineProperty(statics, key, {
        get() {
          return statics.store.getters[key]
        }
      })
    }
  })
}
Copy the code

StaticGetterGenerator execution result Runtime screenshot

5.12 staticMutationGenerator method

Mutations in the store, go to commit in the store for mutation, and namespaced for name/key when the method in statics is accessed.

Sappmodule.xxx () will be called statics.store.mit * after the module is initialized@param module
 * @param modOpt
 * @param statics* /
export function staticMutationGenerator(module, modOpt, statics) {
  // Get the list of Mutations decorators mounted into Mutations
  Object.keys(module.mutations).forEach((key) = > {
    if (module.namespaced) {
      // The method obtained here can be executed directly, equivalent to executing the method directly
      statics[key] = function(. args) {
        statics.store.commit(`${modOpt.name}/${key}`. args) } }else {
      statics[key] = function(. args) { statics.store.commit(key, ... args) } } }) }Copy the code

A screenshot of the staticMutationGenerator running results

5.13 staticActionGenerators

When a statics method is accessed, if it’s an Actions in the store that you’re looking for, go to the Dispatch in the store and submit the corresponding action, and if it’s namespaced, look for the name/key.

/** * Mount module actions to statics, where sappmodule.xxx () can call statics.store.dispatch@param module
 * @param modOpt
 * @param statics* /
export function staticActionGenerators(module, modOpt, statics) {
  Object.keys(module.actions).forEach((key) = > {
    if (module.namespaced) {
      statics[key] = async function(. args) {
        return statics.store.dispatch(`${modOpt.name}/${key}`. args) } }else {
      statics[key] = async function(. args) {
        returnstatics.store.dispatch(key, ... args) } } }) }Copy the code

Screenshot of staticActionGenerators running results

GetModule getModule getModule getModule getModule getModule getModule getModule getModule getModule getModule getModule getModule getModule getModule

6 Vuex-module-decorators calls versus vuex calls

/ / for the stateUserModule.userCode statics.store.state[name?] .userCode/ / get the getterSappModule.currentTab statics.store.getters.[name/?] currentTab/ / called Mutation
SappModule.SET_JDEID(eid)
statics.store.commit([name/?]SET_JDEID, eid)

/ / calls to ActionSappModule.getMenus() statics.store.dispatch([name/?] getMenus)Copy the code

By analyzing the source code, you can see how vuex-module-Decorators are implemented. The state, getter, mutation, and action calls of vuex are proxied to an object via decorators. Using Vuex is as slippery as using object properties/methods.

Advantages:

It is easy to call in the process of use. Keep vuex’s features, such as properties in getModule that are reactive. Type safe type checksum is also available in the IDE with auto-complete/alert/quick location method functions.

Disadvantages:

Vuex has changed the nature of vuex so that object attributes can be assigned directly to getModule. No other @mutation is allowed to be called in the @mutation decorated method, but other mutations and actions can be called in the @action decorated method in a uniform manner.

This points to different methods in different types of decoration, and can be ambiguous when used again.

7 summary

Vuex-module-decorators can be used in most of our daily development scenarios, as long as we pay attention to some boundary conditions when using vuex. Can greatly improve our development efficiency and development experience.