手撕VUE源码(二):vuex的底层实现
let Vue; //vue的构造函数 // modules:{ // state: 1, // modules:{ // a:{ // state:2 // }, // b:{ // state:3 // } // } // } class ModuleCollection{ constructor(options){ this.register([],options); } //深度优先遍历 register(path,rootModule){ //1、定义一个新的子模块 let newModule = { _raw: rootModule, _children: {}, state: rootModule.state } //2、挂载根源模块 if(path.length === 0){ this.root = rootModule } //6、挂载子模块,将此时遍历到的模块挂载到对应父节点上,第二遍递归时才开始走这个判断 if(path.length != 0){ //7、根据path数组,找到rootModule此时对应的父节点 let parent = path.slice(0,-1).reduce((root,current) => { return root._children[current]; },this.root); //8、挂载至父节点 parent._children[path[path.length - 1]] = newModule; } //3、查询是否有子模块 if(rootModule.modules){ //4、对子模块进行遍历 forEach(rootModule.modules,(moduleName,module) => { //5、对子模块进行递归处理 this.register(path.concat(moduleName),module); }) } } } class Store{ //用户传入options,我们首先的任务是将options中的属性和方法,绑定到store自身作用域上 constructor(options){ //先绑定state属性 this._s = new Vue({ //给state加上数据监听,使对应视图实时响应刷新 data: { state: options.state } }) new ModuleCollection(options); //数据层级整理 //getters相当于computed let getters = options.getters || {} this.getters = {}; //再将getters属性中的各个方法绑定到this.getters的中,执行这些方法时自动传入state Object.keys(getters).forEach((getterName => { Object.defineProperties(this.getters,getterName,{ //此处使用箭头函数锁定this上下文,防止因调用者变化使this变更 get:()=>{ return getters[getterName](this.state); } }) })) //mutations相当于method let mutations = options.mutations || {} this.mutations = {}; Object.keys(mutations).forEach(mutationName => { //将私有属性mutations的方法绑定到实例属性this.mutations上 this.mutations[mutationName] = (preload) =>{ //执行私有属性mutations上对应的方法 mutations[mutationName](this.state,preload); } }) } //用户的调用入口 commit = (type,payload) => { this.mutations[type](payload); } //可以通过get、set控制state存取 get state(){ return this._s } } const install = (_Vue) => { Vue = _vue; Vue.mixin({ //声明混入对象的声明周期,在每个组件创建之前加入store beforeCreate(){ //判断父组件还是子组件,如果是子组件则把父组件的store传给子组件 if(this.$options && this.$options.store){ this.$store = this.$options.store }else{ this.$store = this.$parent && this.$parent.$store } } }) } export default { install, //vue.use方法在引入模块时,默认调用模块的install方法 Store }