JavaScript中的模块化之AMD和CMD

498 阅读3分钟
前言:
为什么我们需要模块化开发,模块化开发的好处有哪些? 首先我们先说一下非模块化的开发方式带来的弊端。 非模块化开发中会导致一些问题的出现,变量和函数命名可能相同,会造成变量污染和冲突,并且出错时候很难排查。耦合程度高,不符合软件开发中的高内聚和低耦合的原则,所以我们就可以总结一下模块化开发的好处了:
  • 解决项目中的变量污染问题。
  • 开发效率高,有利于多人协同开发。
  • 职责单一,方便代码重用和维护 。
  • 解决文件依赖问题,无需关注引包顺序 。

模块化开发的演变过程

  • 普通的函数封装
  • 封装成对象
  • 私有公有成员分离 (使用自执行函数,避免变量污染)
  • 模块的维护和扩展(用多个自执行函数把模块分离开来,使用开闭原则—去增添功能,而尽量不要修改原来的代码,)
  • 可以添加模块的第三方依赖(比如添加jQuery的$,$作为一个编程的接口,降低程序之间的耦合度)

模块化的规范

服务端规范 — CommonJs

作为服务器端的开发,不会经过网络传输,所以包的加载几乎都是瞬间完成的,只有加载完成了,才能执行操作,所以commonjs是同步的,nodejs就是实现了这种规范。

浏览器端规范 — AMD 和 CMD

浏览器端的规范,因为所有的文件请求都要经过网络传输,很多文件的加载会有不确定因素存在为了解决这个问题,浏览器端出来了这两个规范。

CMD

CMD在模块定义当中有三个变量,分别是require,exports,module.除了这三个变量可以辨识CMD外,define函数还有一个公有属性define.cmd。我们也可以监测这个值来判断是否是cmd。
如果想要对外提供接口的话,可以将接口绑到exports(module.exports)。

function MyModule() { // ...}
if(typeof module !== `undefined` && typeof exports === `object` && define.cmd) {
     module.exports = MyModule;
}

如果需要支持除了cmd之外的其他符合CommonJS的标准,请去掉define.cmd

AMD

AMD规范中,define函数同样有一个公有属性define.amd。
AMD中的参数便是这个模块的依赖,那么如何在AMD中提供接口呢?它是返回一个对象,这个对象就作为模块的接口,故我们可以这样写:

function MyModule() {
 // ...
}
if(typeof define === `function` && define.amd) { 
    define(function() { 
        return MyModule; 
    });
}

UMD

我们除了提供AMD模块的接口,CMD模块接口,还得提供原生的JS接口

UMD 叫做通用模块定义规范(Universal Module Definition)。也是随着大前端的趋势所诞生,它可以通过运行时或者编译时让同一个代码模块在使用 CommonJs、CMD 甚至是 AMD 的项目中运行。未来同一个 JavaScript 包运行在浏览器端、服务区端甚至是 APP 端都只需要遵守同一个写法就行了。

它没有自己专有的规范,是集结了 CommonJs、CMD、AMD 的规范于一身,我们看看它的具体实现:

((root, factory) => {
    if (typeof define === 'function' && define.amd) {
        //AMD
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        //CommonJS
        var $ = requie('jquery');
        module.exports = factory($);
    } else {
        root.testModule = factory(root.jQuery);
    }
})(this, ($) => {
    //todo
});

总结