最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 怎么实现vuex源码的核心类ModuleCollection

    正文概述 掘金(颜酱)   2020-12-02   455

    本文想写 vuex 源码中核心的一个类,ModuleCollection,这个类是将参数格式化成一定的结构。

    感觉这种模式,在别的源码里也常用,希望自己能真正理解并运用,所以特地有此篇。

    vuex 使用的时候,传入类似这种参数

    export default new Vuex.Store({
      modules: {
        aModule: { state: { msg: "a模块的颜酱" }, },
        bModule: {
          state: { msg: "b模块的颜酱" },
          modules: {
            cModule: { state: { msg: "c模块的颜酱" }, }
          }
        }
      },
      state: { msg: "hi, i am 颜酱" }
    });
    
    

    使用 c 模块的state的时候,是this.$store.state.b.c.msg

    vuex为了方便使用参数,对参数,处理成如下格式,其实就是将父子关系更加明显化。

    {
      "_raw": 根模块数据,
      "state": { "msg": "hi, i am 颜酱", },
      "_children": {
        "aModule": {
          "_raw":a模块的原来数据,
          "state": { "msg": "a模块的颜酱" },
          "_children": {},
        },
        "bModule": {
          "_raw": b模块的原来数据,
          "state": { "msg": "b模块的颜酱" },
          "_children": {
            "cModule": {
              "_raw": c模块的原来数据,
              "state": { "msg": "c模块的颜酱" },
              "_children": {},
            }
          }
        }
      }
    }
    

    每个模块都有的字段:_raw、state、children

    我的思路:简单封装函数,返回正确的格式

    开始,我写了个formatData方法,但不够优雅哇。

    其实这个先试着自己写,处理完第一层逻辑,基本就ok,核心就是递归,只是递归的时候,注意children这个对象,这也算是这里的难点。

    const options = {
      modules: {
        aModule: { state: { msg: "a模块的颜酱" } },
        bModule: {
          state: { msg: "b模块的颜酱" },
          modules: {
            cModule: { state: { msg: "c模块的颜酱" } }
          }
        }
      },
      state: { msg: "hi, i am 颜酱" }
    };
    
    const formatData = (options, res) => {
      res._raw = options;
      res.state = options.state;
      const childModules = options.modules;
      // 如果有模块的话
      if (childModules) {
        // 遍历模块
        Object.keys(childModules).forEach(childModuleName => {
          const curChildModule = childModules[childModuleName];
          // 这边没有的必须先设置{},这样才能赋值属性
          res.children = res.children ? res.children : {};
          res.children[childModuleName] = res.children[childModuleName]
            ? res.children[childModuleName]
            : {};
          // 以上两行代码都是为第二个参数准备的,防止undefined报错
          formatData(curChildModule, res.children[childModuleName]);
        });
      }
    };
    // 因为res是另外的,所以封装个高阶函数,这也使用起来很方便
    const format = options => {
      let res = {};
      formatData(options, res);
      return res;
    };
    console.log(format(options));
    
    

    vuex源码的思路:封装类,以路径的方式注册每个模块

    源码很赞的是,以类的形式出现,核心逻辑是传入模块的路径和模块,来注册在相应的位置。

    比如:根模块有b模块,b模块里有c模块,c模块里有d模块,那么d模块的路径就是[b,c,d]

    注册d模块时候,其实这样就可以:

    root.children.b.children.c.children.d = dModule
    

    当然实际情况是,路径是动态的,需要我们写个函数去实现这样的功能:

    为方便实现,将上面代码拆分下

    // d的父模块
    const parentModule = root.children.b.children.c
    // 父模块里注册当前模块
    parentModule.children.d = dModule
    

    这样拆分的好处是,parentModule可以通过函数很方便的拿到

    function register(path,module){
      const init = root
      const fn = (acc,cur) => acc[children][cur]
      // 父模块,这里想象成上面实际的例子
      const parentModule = path.slice(0,-1).reduce(fn,init)
      // 父模块里注册当前模块
      const curModuleName = path[path.length-1]
      parentModule.children[curModuleName] = module
    }
    

    以上就是最核心的逻辑了,当然这里的root还需要赋值的,其他的两个属性也需要加上,但这都不是事,不另做解释

    const options = {
      modules: {
        aModule: { state: { msg: "a模块的颜酱" } },
        bModule: {
          state: { msg: "b模块的颜酱" },
          modules: {
            cModule: { state: { msg: "c模块的颜酱" } }
          }
        }
      },
      state: { msg: "hi, i am 颜酱" }
    };
    class ModuleCollection {
      constructor(options) {
        // 这里的root存放格式化之后的数据
        this.root = null;
        // 首次注册的是根模块,根模块的路径是空的
        this.register([], options);
      }
      register(path, module) {
        let moduleItem = {
          _raw: module,
          // 注意这里的children是对象
          children: {},
          state: module.state
        };
        // 第一次肯定是根模块,根模块必须赋值
        if (!this.root) {
          this.root = moduleItem;
        } else {
          // 核心代码:根据路径注册模块
          const curModuleName = path[path.length - 1];
          const fn = (acc, cur) => acc.children[cur];
          const parent = path.slice(0, -1).reduce(fn, this.root);
          parent.children[curModuleName] = moduleItem;
        }
        // 当前模块有子模块的话,继续挨个注册
        const childModules = module.modules;
        childModules &&
          Object.keys(childModules).forEach(childModuleName => {
            const curChildModule = childModules[childModuleName];
            // concat用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
            this.register(path.concat(childModuleName), curChildModule);
          });
      }
    }
    
    const modules = new ModuleCollection(options);
    // 这就是格式化之后的数据了
    console.log(modules.root);
    console.log(modules.root.children.bModule.children);
    
    

    起源地下载网 » 怎么实现vuex源码的核心类ModuleCollection

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元