层次和约束:项目中使用vuex的3条优化方案

问题描述

使用vuex的store的过程中,发现了一些不是很优雅的地方:

  1. store层module太多,找state、getter、mutation、action对应的module比较慢。
  1. 组件里面mapGetters、mapActions、mapMutations过多,分不清getter、action、mutation属于哪个module,比较混乱。
  1. 没有使用mutation type 和action type的枚举常量来约束action type和mutation type取值,字符串方式容易出错。(如上图)

解决方案

针对这3个问题,制定了3条重构方案。

1. module聚类分层

按照聚类分层思想,当业务复杂后,需要通过一定的聚类特征对扁平化的结构进行分层。

这里按照数据的用途分了page、components、domain、other这四类,page存储页面组件的数据,components存储基础组件的数据,domain存储实体的数据,other存储其他全局数据。

之前的modules

之后的modules



目前还没有存储实体数据的module,所以暂时为空

2.module添加namespace

store划分module是因为不同的数据有不同的归属。

如果想要每个module都能响应全局action的话,不需要加namespace,但是我们并没有没有一个action对应多个module的action handler的情况。反而因为没有加namespace,导致组件里的多个module的getter、action、mutation都扁平的堆在一起,结构混乱、不清晰。

    ...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'
    }),

一堆的mutation让人迷惑,结构很不清晰,哪个mutation是哪个module必须去store中找。

加上namespace之后,每个mutaion属于一个namespace,每个namespace代表一个module,在组件里就可以轻松的根据namespace区分出哪个module来。

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

这样重构之后,组件用到再多module的action、getter、mutation也不会混乱了。

3.mutation type和action type使用枚举常量约束

mutation type和action type的名字可能会写错,因为没有使用typescript,没有类型约束,如果写错了,编译时无法检查出来,只能在运行时检查。解决这个问题或者使用ts,或者全部的mutation type和action type从枚举常量中取。

store中的数据是模块化的,mutation type 和action type的枚举常量自然也是,但是vuex的module并不会处理这两者,想把这些模块化的motation type和action type挂到store实例上,可以通过vuex插件来解决。

我发现社区并没有我需要的vuex插件,于是我自己封装了一个

/**
 * 生成文件对应的模块
 * 
 * @param {*} dirPath 文件夹路径
 */
const generateModules = (files) => {
    const modules = {}

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


/**
 * 所有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$/)
}

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


/**
 * 根据types获取modules下的多个模块的结构化数据
 * @param {*} types module type
 * @param {*} fieldName 字段名
 */
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;

添加到vuex的plugins中

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

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

module定义时导出mutation types和action types

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

在组件里面就可以使用action type和mutation type来mapAction,mapMutation

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

或者像下面这样全部导入

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

这样就避免了手写字符串可能出错的问题。

总结

针对vuex store的module过多,组件里无法区分出getter、action、mutation属于哪一个module,mutation type和action type无约束这3个问题,针对性的提出了3条解决方案:

module聚类分层,分成page、components、domain、other四个文件夹存放module;

添加namespace,组件中使用mapGetters、mapActions、mapMuatations时加上namespace区分;

module定义时导出mutation types和action types,并通过vuex的插件挂到store上,组件中使用mapMutations和mapActions不再通过字符串取对应值。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • 上一章总结了 Vuex 的框架原理,这一章我们将从 Vuex 的入口文件开始,分步骤阅读和解析源码。由于 Vuex...
    你的肖同学阅读 1,785评论 3 16
  • Vuex 的学习记录 资料参考网址Vuex中文官网Vuex项目结构示例 -- 购物车Vuex 通俗版教程Nuxt....
    流云012阅读 1,455评论 0 7
  • Vuex源码阅读分析 Vuex是专为Vue开发的统一状态管理工具。当我们的项目不是很复杂时,一些交互可以通过全局事...
    steinslin阅读 633评论 0 6
  • 安装 npm npm install vuex --save 在一个模块化的打包系统中,您必须显式地通过Vue.u...
    萧玄辞阅读 2,934评论 0 7
  • vuex是一个状态管理模式,通过用户的actions触发事件,然后通过mutations去更改数据(你也可以说状态...
    Ming_Hu阅读 2,021评论 3 3