在前面,我们已经实现了Vuex中的getters,mutations,actions,现在,我们来介绍以下modules.
对于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
let a = {
state:{
name:'a',
age:18
},
modules:{a1,a2,a3},
getters:{
getAlpha(state){
return state.name
},
getAge(state){
return state.age
}
}
...// mutations
...// actions
}
let b = {
state:{
name:'b'
},
modules:{b1,b2,b3},
getters:{
getName(state){
return state.name
}
}
...// mutations
...// actions
}
let rootModule = {
state:{
name:'iamsmiling'
},
modules:{a,b},
getters{
getName(state){
return state.name
}
}
...// mutations
...// actions
}
// 为了避免代码太过冗余,这里没有给出a1,a2,a3,b1,b2,b3的定义
let store = new Store(rootModule)
根据上面的代码结构,画出我们的state状态树
root
/ \
a b
/ | \ / | \
a1 a2 a3 b1 b2 b3
.....// 当然a1,a2,a3,b1,b2,b3也可能存在子模块
在Vuex中,我们想要调用子模块的getters,我们是这样子进行调用的
store.getters.getAge
咦,等等,root中的getters并没有定义getAge啊,为什么可以这样调用吗,调用形式不应该长这样子的嘛?
store.modules.a.gettters.getAge嘛,搞不懂,搞不懂....
Ok好吧,那我们现在就来解释以,原生Vuex中可以store.getters.getAge这种形式调用的原因,其实也很简单,Vuex不过是将子模块中的state/getters/mutations/actions全部安装到了root上。这样讲或许有些干涩难懂,那么,我们同样以上面的例子来进行说明:
root = { state:{...} ,modules:{a,b},getters:{getName}
a = {state:{...},getters:{getAlpha,getAge}}
从上面可以看出,root中的getters只有getName,a的getters中包括getAlpha,getAge.
所谓模块安装,不过是子模块中的state/getters/mutations/actions定义在root上。
经过模块安装后,在root的getter中,就包含了getName,getAlpha,getAge
在理解上面的东西之后。我们明白,接下来我们的工作就是进行模块安装....
不过,在进行模块安装之前,我们必须先做一项工作,就是进行模块收集,明确具体有哪些模块...
为了方便收集模块,我们来首先对用户传进来的数据进行格式化处理:
{
raw:rootModule,
state:{name:'iamsmiling'}
children:{
a:{
raw:a,
state:{ name:'a',age:18},
children:{a1,a2,a3}
getters:{getAlpha,getAge} //这里进行了简写
}
b:{
raw:b,
state:{name:'b'},
children:{b1,b2,b3}
getters:{getName} //这里进行了简写
}
},
getters:{getName}//这里进行了简写
}
定义一个类,进行模块收集
class ModuleCollection{
constructor(options){
this.register([],options) // 注册模块
}
register(path,rootModule){
let newModule = {
raw: rootModule,
children:{},
state:rootModule.state
}
if(!path.length){
this.root = newModule
}else{
let parent = path.slice(0,-1).reduce((root,current)=>{
return this.root.children[current]
},this.root)
parent.children[path[path.length-1]] = newModule
}
if(rootModule.modules){//如果存在modules选项说明存在子模块
forEach(rootModule.modules,(name,module)=>{
this.register(path.concat(name),module) // 使用递归收集模块
// 使用例子解析
// 第一次遍历 this.register([a],a)
// newModule={raw:a,children:{},state:a.state}
// path.silce(0,-1) ==> []
// parent = this.root
// this.root[a] =a
// this.register([a,a1],a1)
// newModule = {raw:a1,children:{},state:a1.state}
// path.slice(0,-1) = [a]
//[a].reduce ===> this.root.children[a]
// parent = a
...
// 第二次遍历 this.register([b],b)
// newModule={raw:b,children:{},state:b.state}
// path.slice(0,-1) ==> [a]
// parent = this.root
// this.root[b] = b
...
})
}
}
}
注意事项
concat返回新数组,slice返回新数组,不改变原数组reduce,如果数组为空,会直接返回我们传入的初始值
到目前为止,模块收集功能也实现了...