前言
- 文章结构采用【指出阶段目标,然后以需解决问题为入口,以解决思路为手段】达到本文目标,若使诸君稍有启发,不枉此文心力^-^
- 此文是vuex系列第二版,建议从第一版开始食用Vuex | 思路篇 | 初版,文首均有完成功能目录,可看是否需要
完成功能
- vuex数据模块化
待完成功能
- 辅助函数map系列(mapState、mapGetters、mapMutations、mapActions)
- vuex插件机制
第一阶段:处理模块化--规格化
先看使用
modules: {
a: {
state: {
count:201
},
modules: {
b: {
state: {
count:300
},
mutations: {
add(state){
console.log("b的add");
}
}
}
}
}
}
核心问题
- 多层嵌套module
解决思路
- 规格化+递归:其实我们可以养成一个思维惯性,这类需要深度递归的问题都可以采用这个方式,比如vue-router亦是如此
- 先规定一个数据结构,vuex中定义是一个对象newModule,属性如下
- _raw:object类型,当前子模块配置信息对象
- _children:object,其下子模块信息,key为模块名(即a),val为子模块对应的newModule
- state:obj,子模块的state对象
- 深度递归,首先,我们需要将模块挂载到对应父节点上,所以需要一个路径,vuex中采用数组;其次需要module对象;所以我们的递归入口函数
- 需输入:path--父模块路径数组(如对于b模块而言,为[a]);module--用户定义对象
- 需执行:
- 组装newModule对象
- 判断是否为最外层,是则挂载在root属性上;不是挂载在path中对应的父模块的_children上
- 判断有无modules属性,有则遍历递归(因为可能有多个子模块)
- 先规定一个数据结构,vuex中定义是一个对象newModule,属性如下
第二阶段:处理模块化的actions、mutations
先看使用
注意我们在根上定义了一个mutation为add,同时在a模块下的b模块也定义了一个同名的mutation
# store.js
export default new Vuex.Store({
state: {
count: 100
},
mutations: {
add(state){
state.a.b.count += 1;
console.log("root的add");
}
},
actions: {
add({commit}){
commit('add')
// console.log(commit);
// state.count += 1;
}
},
modules: {
a: {
state: {
count:201
},
modules: {
b: {
state: {
count:300
},
mutations: {
add(state){
console.log("b的add");
}
}
}
}
}
}
})
此时页面点击按钮以action触发commit('add'),会发现很神奇的事情
# app.vue
<button @click="clickMe">+1</button>
methods: {
clickMe(){
this.$store.dispatch('add')
}
}
页面打印了两个log,也就是意味着都被触发了
也就是说明,对于mutations而言,后代模块同样会被收集,在触发时批量触发;actions同理
核心问题
- 如何实现
解决思路
一次触发,多次执行;一对多优先考虑订阅发布;
- 改写第一版逻辑,订阅时采用数组存储回调,先将后代模块的同名mutation/action收集到一个数组中
- 触发时即遍历执行
第三阶段:处理模块化的state
先看使用
# store.js
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const moduleC = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
modules: {
b: moduleB
}
}
const store = new Vuex.Store({
modules: {
a: moduleA,
c: moduleC
}
})
store.state.a // -> moduleA 的状态
store.state.c.b // -> moduleB 的状态
核心问题
- 实现挂载效果
- 注意实现子模块数据的改变要驱动视图的改变,即vue托管数据
解决思路
- installModule过程中,如果path存在(即有子模块),则将数据挂载在父模块上,key为子模块名,val为state
- 挂载时不要直接挂载,对象属性的增删不会为vue所监听,可以使用Vue.set的方式进行设置(原因可见拙作(vue1.0版)数据观测 依赖收集))