背景
之前公司业务做了一个审核功能,业务数据和状态比较复杂,当初做的时候用上了 Vuex store 进行数据交互,最近新上了基于审核功能页面模板的需求,依照其模板开发不同的功能(不同菜单入口,模板相同,只有api不相同),在改造的过程中遇到了 store 组件如何进行复用的问题。
问题
审核系统是可以菜单多开的,也就是同一个模板生成的菜单需要具备同时打开使用的功能,我们知道 vue 中组件是可以复用的,每次引用都是生成一个新的实例,但组件中引入了 store 后导致不同实例共享同个 store,数据就会产生混淆
方法一:改造成互斥菜单
当模板菜单中其中一个已经打开的情况下,另外模板菜单不能继续打开,并弹出提示需要先关闭已打开的xx菜单
,需要用this.$router.push
替代<router-link>
,监听绑定事件@click
判断是否需要新开菜单后执行 push 操作,此方法限制了多开页面的功能
方法二:尝试不用菜单引用不同store
然后我就开始思考,是否可以通过动态修改mapState
、mapActions
、mapMutations
的首个参数(muduleName)达到引用不同 store 呢?
但mapState
首个参数不能获取 this,所以不能动态修改 moduleName;那是否可以不用mapxxx
函数直接调用呢,答案是可以的,但是需要修改的地方太多,不利于做改造
方法三:尝试动态注册
https://vuex.vuejs.org/zh/guide/modules.html#%E6%A8%A1%E5%9D%97%E5%8A%A8%E6%80%81%E6%B3%A8%E5%86%8C
动态注册也是可行的,但需要注册不一样的 moduleName,然后问题还是回到了方法二,只是从静态注册变成了动态注册
方法四:组件注册store
和方法三不一样的是,方法三是注册 module,而这是注册整个 store
我们可以通过注册新 store 的方式注册 module,并且 module 会变为 组件内全局 store,这个方法只需要将原本引用的 moduleName 去掉,其他代码可以维持不动,最低成本达到 store 组件复用
ps:如果需要引用外部 store,可用
this.$root.$store
访问,mapxxx
方法不可访问外部 store
总结
如果在设计之初就知道需要复用组件的话,组件内最好不要用 store,因为 store 的用处是共享整个 vue 应用数据,方法四虽然可行但却破坏了这种模式,导致外部组件无法访问其内部重新注册的 store(当然可以通过动态注册module的方式注册进全局 store)
[更新]通过动态注册module的方式注册进全局 store
// 统一管理的mixin
import Base from '@/page/base';
import module from '@/store/modules/module';
import Vuex from 'vuex';
export default function(menuName) {
return {
store: new Vuex.Store(module),
components: { Base },
created() {
const rootStore = this.$root.$store;
// 动态注册module,此时两份state不同步
rootStore.registerModule(menuName, {
...module,
namespaced: true,
});
// 同步更新rootStore.state[menuName]
this.$store.subscribe(mutation => {
rootStore.commit(`${menuName}/${mutation.type}`, mutation.payload);
});
},
};
}
作者其他文章:
关于element el-button使用$attrs的一个注意要点
github: https://github.com/masongzhi