AMD && CMD

前言

JavaScript初衷:实现简单的页面交互逻辑,寥寥数语便可;javascript

随着web2.0时代的到来,Ajax技术获得普遍应用,jQuery等前端库层出不穷,前端代码日益膨胀html

问题:前端

这时候JavaScript做为嵌入式的脚本语言的定位动摇了,JavaScript却没有为组织代码提供任何明显帮助,甚至没有类的概念,JavaScript极其简单的代码组织规范不足以驾驭如此庞大规模的代码java

1、模块

模块化:是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。node

在理想状态下咱们只须要完成本身部分的核心业务逻辑代码,其余方面的依赖能够经过直接加载被人已经写好模块进行使用便可。jquery

无模块git

<script src="jquery.js"></script>   
<script src="jquery_scroller.js"></script>   
<script src="main.js"></script>   
<script src="other1.js"></script>   
<script src="other2.js"></script>   
<script src="other3.js"></script>

  

优势:github

相比于使用一个js文件,这种多个js文件实现最简单的模块化的思想是进步的。 web

缺点:编程

污染全局做用域。 由于每个模块都是暴露在全局的,简单的使用,会致使全局变量命名冲突,固然,咱们也可使用命名空间的方式来解决。
对于大型项目,各类js不少,开发人员必须手动解决模块和代码库的依赖关系,后期维护成本较高。
依赖关系不明显,不利于维护。 好比main.js须要使用jquery,可是,从上面的文件中,咱们是看不出来的,若是jquery忘记了,那么就会报错。

 

2、CommonJS

CommonJs 是服务器端模块的规范,Node.js采用了这个规范。

根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。

var math = require('math');
math.add(2, 3);

 

第二行math.add(2, 3),在第一行require('math')以后运行,所以必须等math.js加载完成。也就是说,若是加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的。

CommonJS 加载模块是同步的,因此只有加载完成才能执行后面的操做。像Node.js主要用于服务器的编程,加载的模块文件通常都已经存在本地硬盘,因此加载起来比较快,不用考虑异步加载的方式,因此CommonJS规范比较适用。但若是是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。因此就有了 AMD  CMD 解决方案。

 

3、AMD

AMD 即 Asynchronous Module Definition,中文名是异步模块定义的意思。它是一个在浏览器端模块化开发的规范

AMD也采用require()语句加载模块,可是不一样于CommonJS,它要求两个参数:

require([module], callback);

 

第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功以后的回调函数。若是将前面的代码改写成AMD形式,就是下面这样:

require(['math'], function (math) {
  math.add(2, 3);
});

 

math.add()与math模块加载不是同步的,浏览器不会发生假死。因此很显然,AMD比较适合浏览器环境。目前,主要有两个Javascript库实现了AMD规范:require.jscurl.js

与 RequireJS

AMD 是 RequireJS 在推广过程当中对模块定义的规范化产出

AMD异步加载模块。它的模块支持对象 函数 构造器 字符串 JSON等各类类型的模块。

//经过数组引入依赖 ,回调函数经过形参传入依赖
define(['Module1', ‘Module2’], function (Module1, Module2) {

    function foo () {
        /// someing
        Module1.test();
    }

    return {foo: foo}
});

 

  • 第一个参数 id 为字符串类型,表示了模块标识,为可选参数。若不存在则模块标识应该默认定义为在加载器中被请求脚本的标识。若是存在,那么模块标识必须为顶层的或者一个绝对的标识。
  • 第二个参数,dependencies ,是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。
  • 第三个参数,factory,是一个须要进行实例化的函数或者一个对象。
    建立模块标识为 Module1 的模块,依赖于 require, export,和标识为 beta 的模块  

AMD规范容许输出模块兼容CommonJS规范,这时define方法以下:

define(function (require, exports, module) {
    
    var reqModule = require("./someModule");
    requModule.test();
    
    exports.asplode = function () {
        //someing
    }
});

 

优势:

适合在浏览器环境中异步加载模块。能够并行加载多个模块。
缺点:

提升了开发成本,而且不能按需加载,而是必须提早加载全部的依赖。

 

4、CMD

CMD是SeaJS 在推广过程当中对模块定义的规范化产出

CMD和AMD的区别有如下几点:

1.对于依赖的模块AMD是提早执行,CMD是延迟执行。不过RequireJS从2.0开始,也改为能够延迟执行(根据写法不一样,处理方式不经过)。

2.AMD推崇依赖前置(在定义模块的时候就要声明其依赖的模块),CMD推崇依赖就近(只有在用到某个模块的时候再去require——按需加载)。

//AMD
define(['./a','./b'], function (a, b) {

    //依赖一开始就写好
    a.test();
    b.test();
});

//CMD
define(function (requie, exports, module) {
    
    //依赖能够就近书写
    var a = require('./a');
    a.test();
    
    ...
    //软依赖
    if (status) {
    
        var b = requie('./b');
        b.test();
    }
});

 

3.AMD的api默认是一个当多个用,CMD严格的区分推崇职责单一。例如:AMD里require分全局的和局部的。CMD里面没有全局的 require,提供 seajs.use()来实现模块系统的加载启动。CMD里每一个API都简单纯粹。

AMD 是 RequireJS 在推广过程当中对模块定义的规范化产出,CMD是SeaJS 在推广过程当中被普遍认知。RequireJs出自dojo加载器的做者James Burke,SeaJs出自国内前端大师玉伯。两者的区别,玉伯在12年如是说

RequireJS 和 SeaJS 都是很不错的模块加载器,二者区别以下:

1. 二者定位有差别。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。SeaJS 则专一于 Web 浏览器端,同时经过 Node 扩展的方式能够很方便跑在 Node 服务器端

2. 二者遵循的标准有差别。RequireJS 遵循的是 AMD(异步模块定义)规范,SeaJS 遵循的是 CMD (通用模块定义)规范。规范的不一样,致使了二者API 的不一样。SeaJS 更简洁优雅,更贴近 CommonJS Modules/1.1 和 Node Modules 规范。

3. 二者社区理念有差别。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。SeaJS 不强推,而采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。

4. 二者代码质量有差别。RequireJS 是没有明显的 bug,SeaJS 是明显没有 bug。

5. 二者对调试等的支持有差别。SeaJS 经过插件,能够实现 Fiddler 中自动映射的功能,还能够实现自动 combo 等功能,很是方便便捷。RequireJS无这方面的支持。

6. 二者的插件机制有差别。RequireJS 采起的是在源码中预留接口的形式,源码中留有为插件而写的代码。SeaJS 采起的插件机制则与 Node 的方式一致开放自身,让插件开发者可直接访问或修改,从而很是灵活,能够实现各类类型的插件。

 

 

优势:
一样实现了浏览器端的模块化加载。
能够按需加载,依赖就近。

缺点:
依赖SPM打包,模块的加载逻辑偏重。

 

 5、 ES6 模块

CommonJS

  • 对于基本数据类型,属于复制。即会被模块缓存。同时,在另外一个模块能够对该模块输出的变量从新赋值。
  • 对于复杂数据类型,属于浅拷贝。因为两个模块引用的对象指向同一个内存空间,所以对该模块的值作修改时会影响另外一个模块。
  • 当使用require命令加载某个模块时,就会运行整个模块的代码。
  • 当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块不管加载多少次,都只会在第一次加载时运行一次,之后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
  • 循环加载时,属于加载时执行。即脚本代码在require的时候,就会所有执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

ES6模块

  • ES6模块中的值属于【动态只读引用】。
  • 对于只读来讲,即不容许修改引入变量的值,import的变量是只读的,不管是基本数据类型仍是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
  • 对于动态来讲,原始值发生变化,import加载的值也会发生变化。不管是基本数据类型仍是复杂数据类型。
  • 循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就可以执行。

 

参考:

关于 CommonJS AMD CMD UMD

JavaSript模块规范 - AMD规范与CMD规范介绍

JavaScript模块化 --- Commonjs、AMD、CMD、ES6 modules

相关文章
相关标签/搜索