vuex的简易实现

128 阅读2分钟

前言

用vuex也有一段时间了,今天手撸一个简易的vuex。

1.install

vue暴露的插件系统,使用Vue.use()时必须提供一个install函数注入Vue

`//声明一个全居变亮保存Vue
let Vue = null;
const install = _Vue => {
  Vue = _Vue;
  Vue.mixin({
    //注册一个全局混入的beforeCreate
    beforeCreate() {
      const options = this.$options;
      const { store } = options;
      if (store) {
        //当前组件存在store则直接赋值
        this.$store = typeof store === 'function' ? store() : store;
      } else if (options.parent && options.parent.$store) {
        //子组件从父组件拿$store
        this.$store = options.parent.$store;
      }
    },
  });
};`

2. store

//准备一个简易的store类
const store = function(options) {
  const { state = {}, mutations = {}, actions = {}, getters = {} } = options || {};
  //使用Object.create(null)创建一个纯净的对象,传null的话没有__proto__
  //借助已保存的Vue的observable方法将state转化为响应式对象
  this.state = Vue.observable(state);
  this.mutations = Object.create(null);
  this.actions = Object.create(null);
  this.getters = Object.create(null);
  //使用箭头函数,绑定this方便解构使用
  this.dispath = () => {};
  this.commit = () => {};
  //modules待补
};


3. mutations

//遍历传入的mutations
Object.keys(mutations).forEach(mutation => {
  //使用...接收所有参数,mutations第一个参数固定为state
  this.mutations[mutation] = (...params) => {
    mutations[mutation](this.state, ...params);
  };
});

4. actions

//遍历传入的actions
Object.keys(actions).forEach(action => {
  //使用...接收所有参数,actions需要使用commit提交第一个参数固定为当前实例
  this.actions[action] = (...params) => {
  //返回调用结果方便移步操作
    return actions[action](this, ...params); 
  };
});

5. getters

//遍历传入的getters
Object.keys(getters).forEach(getter => {
  //getters比较特殊需要做一步代理操作,第一个参数固定为state
  Object.defineProperty(this.getters, getter, {
    get: () => {
      return getters[getter](this.state);
    },
  });
});

6. dispath

//dispath做一步promise包装
this.dispath = (action, ...params) => {
  return new Promise(resolve => {
    //取调用结果简单判断下是否为promise
    const res = this.actions[action](...params);
    const { then } = res || {};
    if (then) {
      return then.call(res, () => {
        resolve();
      });
    }
    resolve();
  });
};

7. commit

//commit 提交mutation
this.commit = (mutation, ...params) => {
  this.mutations[mutation](...params);
};

8. map映射辅助函数

//map传参有两种形式Array及obj,准备一个工具函数解析map传参,暂时不做module处理
const normalizeMap = params => {
  switch (Object.prototype.toString.call(params)) {
    case '[object Object]':
      return Object.keys(params).map(key => ({ key, val: params[key] }));
    case '[object Array]':
      return params.map(key => ({ key, val: key }));
    default:
      //抛出异常
      throw new Error('参数必须为数组或对象');
  }
};
//mapState
const mapState = params => {
  return normalizeMap(params).reduce((res, { key, val }) => {
    //此处this指向组件实例
    res[key] = function() {
      return typeof val === 'function' ? val(this.$store.state) : this.$store.state[val];
    };
    return res
  }, {});
};
//mapGetters
const mapGetters = params => {
  return normalizeMap(params).reduce((res, { key, val }) => {
    //此处this指向组件实例
    res[key] = function() {
      return this.$store.getters[val];
    };
    return res
  }, {});
};
//mapActions
const mapActions = params => {
  return normalizeMap(params).reduce((res, { key, val }) => {
    //此处this指向组件实例
    res[key] = function(...data) {
      return this.$store.dispath(val, ...data);
    };
    return res
  }, {});
};
//mapMutations
const mapMutations = params => {
  return normalizeMap(params).reduce((res, { key, val }) => {
    //此处this指向组件实例
    res[key] = function(...data) {
      this.$store.commit(val, ...data);
    };
    return res
  }, {});
};

9. modules

待补

10. 完整代码

let Vue = null;
const install = _Vue => {
  Vue = _Vue;
  Vue.mixin({
    //注册一个全局混入的beforeCreate
    beforeCreate() {
      const options = this.$options;
      const { store } = options;
      if (store) {
        //当前组件存在store则直接赋值
        this.$store = typeof store === 'function' ? store() : store;
      } else if (options.parent && options.parent.$store) {
        //子组件从父组件拿$store
        this.$store = options.parent.$store;
      }
    },
  });
};

//准备一个简易的store类
const store = function(options) {
  const { state = {}, mutations = {}, actions = {}, getters = {} } = options || {};
  //使用Object.create(null)创建一个纯净的对象,传null的话没有__proto__
  //借助已保存的Vue的observable方法将state转化为响应式对象
  this.state = Vue.observable(state);
  this.mutations = Object.create(null);
  this.actions = Object.create(null);
  this.getters = Object.create(null);
  //modules待补
  this.modules = Object.create(null);
  //遍历传入的mutations
  Object.keys(mutations).forEach(mutation => {
    //使用...接收所有参数,mutations第一个参数固定为state
    this.mutations[mutation] = (...params) => {
      mutations[mutation](this.state, ...params);
    };
  });
  //遍历传入的actions
  Object.keys(actions).forEach(action => {
    //使用...接收所有参数,actions需要使用commit提交第一个参数固定为当前实例
    this.actions[action] = (...params) => {
      //返回调用结果方便移步操作
      return actions[action](this, ...params);
    };
  });
  //遍历传入的getters
  Object.keys(getters).forEach(getter => {
    //getters比较特殊需要做一步代理操作,第一个参数固定为state
    Object.defineProperty(this.getters, getter, {
      get: () => {
        return getters[getter](this.state);
      },
    });
  });
  //使用箭头函数,绑定this方便解构使用
  //dispath做一步promise包装
  this.dispath = (action, ...params) => {
    return new Promise(resolve => {
      //取调用结果简单判断下是否为promise
      const res = this.actions[action](...params);
      const { then } = res || {};
      if (then) {
        return then.call(res, () => {
          resolve();
        });
      }
      resolve();
    });
  };
  //commit 提交mutation
  this.commit = (mutation, ...params) => {
    this.mutations[mutation](...params);
  };
};

//map传参有两种形式Array及obj,准备一个工具函数解析map传参,暂时不做module处理
const normalizeMap = params => {
  switch (Object.prototype.toString.call(params)) {
    case '[object Object]':
      return Object.keys(params).map(key => ({ key, val: params[key] }));
    case '[object Array]':
      return params.map(key => ({ key, val: key }));
    default:
      //抛出异常
      throw new Error('参数必须为数组或对象');
  }
};
//mapState
const mapState = params => {
  return normalizeMap(params).reduce((res, { key, val }) => {
    //此处this指向组件实例
    res[key] = function() {
      return typeof val === 'function' ? val(this.$store.state) : this.$store.state[val];
    };
    return res;
  }, {});
};
//mapGetters
const mapGetters = params => {
  return normalizeMap(params).reduce((res, { key, val }) => {
    //此处this指向组件实例
    res[key] = function() {
      return this.$store.getters[val];
    };
    return res;
  }, {});
};
//mapActions
const mapActions = params => {
  return normalizeMap(params).reduce((res, { key, val }) => {
    //此处this指向组件实例
    res[key] = function(...data) {
      return this.$store.dispath(val, ...data);
    };
    return res;
  }, {});
};
//mapMutations
const mapMutations = params => {
  return normalizeMap(params).reduce((res, { key, val }) => {
    //此处this指向组件实例
    res[key] = function(...data) {
      this.$store.commit(val, ...data);
    };
    return res;
  }, {});
};

export { store, mapState, mapGetters, mapActions, mapMutations,install };