Hierarchy and Constraints: Three Optimizations for Using vuex in Projects

Keywords: TypeScript

Problem description

In the process of using the store of vuex, we found some not very elegant points:

  1. There are too many modules in the store layer, so it is slow to find the corresponding modules of state, getter, mutation and action.

  1. There are too many mapGetters, mapActions, and mapMutations in the component, and it is confusing to know which module getter, action and mutation belong to.

  1. There is no use of mutation type and action type enumeration constants to constrain action type and mutation type values, and string mode is prone to errors. (as shown above)

Solution

Aiming at these three problems, three reconstruction schemes are formulated.

1. module clustering hierarchy

according to Clustering Hierarchy When the business is complex, the flat structure needs to be stratified by certain clustering features.

There are four types of data: page, components, domain and other. Page stores data of page components, components stores data of basic components, domain stores data of entities, and other stores other global data.

Previous modules

Subsequent modules

At present, there is no module for storing entity data, so it is empty for the time being.

2.module adds namespace

store partitions module s because different data have different attributions.

If you want every module to respond to global actions, you don't need namespace, but we don't have an action handler that corresponds to multiple modules. On the contrary, without namespace, the getter, action and mutation of multiple modules in the component are stacked flat together, and the structure is chaotic and unclear.

    ...mapMutations([
      changeisIceTree: 'changeisIceTree',
      changeIceTreeStatus: 'changeIceTreeStatus',
      showToast: 'showToast',
      changeremainingfronzeTime: 'changeremainingfronzeTime',
      decreaseremainingfronzeTime: 'decreaseremainingfronzeTime',
      changeiceTreeFadeout: 'changeiceTreeFadeout',
      changeiceTreeFadeIn: 'changeiceTreeFadeIn',
      changefrozenTimes: 'changefrozenTimes',
      changetreecurTime: 'changetreecurTime',
      changequickTreeMedal:'changequickTreeMedal',
      changequickHonorMedal:"changequickHonorMedal",
      upDatePopUpOptionStatus: 'upDatePopUpOptionStatus'
    }),

A bunch of mutations is confusing, the structure is not clear, which mutation is which module must be found in the store.

After adding namespace, each mutaion belongs to a namespace, and each namespace represents a module. It is easy to distinguish which module is based on namespace in components.

...mapGetters('aaaaa',[
   'mutation111111',
   'mutation22222',
   'mutation33333'
]);
...mapMutations('aaaaa',[
   'mutation111111',
   'mutation22222',
   'mutation33333'
]);
...mapMutations('bbbbb',[
   'mutation4444444',
   'mutation555555',
   'mutation666666',
]);

After this refactoring, the action, getter, mutation of the component using more module s will not be confused.

3.mutation type and action type use enumeration constraints

The names of mutation type and action type may be written incorrectly because they are not typescript and have no type constraints. If they are written incorrectly, they cannot be checked at compile time and can only be checked at run time. To solve this problem, either ts, or all mutation types and action types are extracted from the enumeration constants.

The data in the store is modular, so are the enumeration constants of mutation type and action type, but the module of vuex does not deal with them. If you want to attach these modular motation type s and action types to the store instance, you can use the vuex plug-in to solve them.

I found that the community didn't have the vuex plug-ins I needed, so I wrapped one myself.

/**
 * Modules for Generating Files
 * 
 * @param {*} dirPath Folder Path
 */
const generateModules = (files) => {
    const modules = {}

    files.keys().forEach(key => {
        modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
    })
    return modules;
}


/**
 * All file
 * 
 */
const allFiles = {
    page: require.context('../modules/page', false, /\.js$/),
    components: require.context('../modules/components', false, /\.js$/),
    domain: require.context('../modules/domain', false, /\.js$/),
    other: require.context('../modules/other', false, /\.js$/)
}

/**
 * All module
 * 
 */
const allModules = {
    page: generateModules(allFiles.page),
    components: generateModules(allFiles.components),
    domain: generateModules(allFiles.domain),
    other: generateModules(allFiles.other)
}


/**
 * Obtaining structured data of multiple modules under modules according to types
 * @param {*} types module type
 * @param {*} fieldName Field name
 */
const getStructuredData = (types, fieldNames) => {
    const structuredData = {};
    types.forEach(type => {
        const modules = allModules[type];
        const structuredModuleData = Object.keys(modules).map(moduleName => {
            const fields = fieldNames.map(fieldName => modules[moduleName][fieldName])
            return {
                [moduleName]: Object.assign(...fields)
            }
        });
        structuredData[type]= structuredModuleData && structuredModuleData.length ? Object.assign(...structuredModuleData): {};
    })
    return structuredData
}


const enumTypePlugin = store => {

    const mutationTypeEnum = getStructuredData(['page','components','domain','other'], ['mutationTypes']);
    const actionTypeEnum = getStructuredData(['page','components','domain','other'], ['actionTypes']);

    store.mutationTypes = mutationTypeEnum;
    store.actionTypes = actionTypeEnum;
}
module.exports = enumTypePlugin;

Add to vuex plugins

import typeEnumPlugin from './type-enum-plugin';

new Vuex.Store(
  modules,
  plugins: [typeEnumPlugin]
)

Export mutation types and action types when module is defined

module.exports = {
   state,
   getters,
   mutations,
   actions,
   mutationTypes,
   actionTypes
}

Within the component, you can use action type and mutation type to map action, map Mutation

...mapActions({
  mutation1: this.$store.mutationTypes.page.aaa.mutation1,
  mutation2: this.$store.mutationTypes.page.aaa.mutation2,
  mutation3: this.$store.mutationTypes.page.aaa.mutation3
})
...mapActions({
  action1: this.$store.actionTypes.page.aaa.action1,
  action2: this.$store.actionTypes.page.aaa.action2,
  action3: this.$store.actionTypes.page.aaa.action3
})

Or all imports as follows

...mapMutations(this.$store.mutationTypes.page.aaa)
...mapActions(this.$store.actionTypes.page.aaa)

This avoids the possibility of errors in handwritten strings.

summary

Aiming at the problem that there are too many modules in vuex store, which module getter, action and mutation belong to, mutation type and action type are unconstrained, three solutions are put forward.

module clustering is divided into four folders: page, components, domain and other.

Adding namespace, the component uses mapGetters, mapActions, mapMuatations with namespace distinction;

When module is defined, mutation types and action types are exported and linked to the store through the plug-in of vuex, and mapMutations and mapActions are used in the component instead of taking corresponding values through strings.

Posted by Calgaryalberta on Sun, 22 Sep 2019 20:31:50 -0700