手撕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
}

 

posted @ 2020-04-02 02:08  姜小希  阅读(756)  评论(0编辑  收藏  举报