基于Vuex从零实现自己的Vuez插件-moduleCollection(五)

218 阅读3分钟

在前面,我们已经实现了Vuex中的getters,mutationsactions,现在,我们来介绍以下modules.
对于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 statemutationactiongetter、甚至是嵌套子模块——从上至下进行同样方式的分割:

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,如果数组为空,会直接返回我们传入的初始值
    到目前为止,模块收集功能也实现了...