Vuex 的模块,理论上是支持无限层级递归的树型结构;
但当前版本的 Vuex 仅仅处理了单层 options 对象;
因此,需要继续添加支持深层处理的递归逻辑,从而完成“模块树”的构建,即:实现 Vuex 的模块收集;
为了能够模拟出多层级的“模块树”,我们再创建一个模块 ModuleC,并注册到 ModuleA 下;
模块层级设计:index 根模块中包含模块 A、模块 B;模块 A 中,又包含了模块 C;
modules: { // 子模块
aCount: {
namespaced: true,
state: { count: 0 },
mutations: {
add(state, payload) { // aCount/add
state.count += payload
}
},
modules: {
cCount: {
namespaced:true,
state: { count: 0 },
mutations: {
add(state, payload) { // aCount/cCount
state.count += payload
}
},
}
}
},
bCount: {
state: { count: 0 },
namespaced: true,
mutations: {
add(state, payload) {
state.count += payload
}
},
}
}
我们期望最后实现的结构如下
{
_raw: '根模块',
_children:{
moduleA:{
_raw:"模块A",
_children:{
moduleC:{
_raw:"模块C",
_children:{},
state:'模块C的状态'
}
},
state:'模块A的状态'
},
moduleB:{
_raw:"模块B",
_children:{},
state:'模块B的状态'
}
},
state:'根模块的状态'
}
在vuex下创建Modules文件夹,创建module-collection.js文件,创建 ModuleCollection 类:用于在 Vuex 初始化时进行模块收集操作;
export default class ModuleCollection {}
模块收集的操作,就是(深度优先)递归地处理 options 选项中的 modules 模块,构建成为树型结构:
创建 register 方法:携带模块路径,对当前模块进行注册,执行“模块树”对象的构建逻辑;
register(rawModule, path) {
const newModule = new Module(rawModule);
// rawModule.newModule = newModule; // 把新的模块 添加到原始对象上
if (path.length == 0) { // 是一个根模块
this.root = newModule
} else {
/**
* 当path为['aCount','cCount']时,path.slice(0, -1)为aCount,截取掉数组的最后一个
* reduce中的current为aCount,module为传入的this.root
* 第一次aCount时,将aCount赋值给了this.root,所以在此处的parent一定是子元素的父元素
*/
const parent = path.slice(0, -1).reduce((module, current) => {
return module.getChild(current)
}, this.root);
parent.addChild(path[path.length - 1], newModule)
}
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(rawChildModule, path.concat(key))
})
}
console.log(newModule)
return newModule
}
递归当前树中的state、getter、mutation、action 定义到当前 store 实例中
在 Store 类中,创建 installModule 模块安装方法:对当前模块对象进行递归处理;
Module 类:src/vuex/modules/module.js
// src/vuex/modules/module.js
/**
* Module 模块类,提供模块数据结构与相关能力扩展
*/
class Module {
constructor(newModule) {
this._raw = newModule;
this._children = {};
this.state = newModule.state
}
/**
* 根据模块名获取模块实例
* @param {*} key 模块名
* @returns 模块实例
*/
getChild(key) {
return this._children[key];
}
/**
* 向当前模块实例添加子模块
* @param {*} key 模块名
* @param {*} module 子模块实例
*/
addChild(key, module) {
this._children[key] = module
}
/**
* 遍历当前模块的子模块,具体处理由外部回调实现
* @param {*} fn 返回当前子模块 和 key,具体处理逻辑由调用方实现
*/
forEachChild(fn) {
Object.keys(this._children).forEach(key=>fn(this._children[key],key));
}
}
export default Module;
组合state,通过path.slice(0, -1).reduce
的方法,组装父子树
// 定义状态
const state = store._modules.root.state; // 根状态
installModule(store, state, [], store._modules.root);
function installModule(store, rootState, path, module) { // 递归安装
let isRoot = !path.length; // 如果数组是空数组 说明是根,否则不是
if (!isRoot) { // []
/**
* path为[aCount,cCount],state[key]为aCount的值{count:0}
* path[path.length-1]为cCount,将cCount作为键,赋值给aCount,此时的module为cCount的值,module.state为cCount的值{count:0}
*/
let parentState = path.slice(0, -1).reduce((state, key) => state[key], rootState);
parentState[path[path.length-1]] = module.state
}
module.forEachChild((child, key) => { // aCount,bCount
installModule(store, rootState, path.concat(key), child);
})
}