AMD和CMD规范

1.名词解释
AMD:Asynchronous Modules Definition异步模块定义,提供定义模块及异步加载该模块依赖的机制。
CMD:Common Module Definition 通用模块定义,提供模块定义及按需执行模块javascript

RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD (通用模块定义)规范,node.js遵循CommonJS规范。规范的不一样,致使了二者 API 不一样。html

2. 提早执行:提早异步并行加载
优势:尽早执行依赖能够尽早发现错误;缺点:容易产生浪费
3. 延迟执行:延迟按需加载
优势:减小资源浪费 缺点:等待时间长、出错时间延后java

2.1 AMD与CMD代码模式node

AMD代码模式-运行策略jquery

define(['./a', './b'], function(a, b) { //运行至此,a.js和b.js已经下载完成 a模块和b模块已经执行完,直接可用;
    a.doing();
    // 此处省略500行代码
    b.doing();
});

CMD代码模式-运行策略npm

define(function(require, exports, module) {
     var a = require("./a"); //等待a.js下载、执行完
     a.doing();
     // 此处省略500行代码
     var b = require("./b"); //依赖就近书写
     b.doing();
});

3. AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。好比 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每一个 API 都简单纯粹。编程

方案 | 优点 | 劣势 | 特色
AMD | 速度快 | 会浪费资源 | 预先加载全部的依赖,直到使用的时候才执行
CMD | 只有真正须要才加载依赖 | 性能较差 | 直到使用的时候才定义依赖数组

它们除了但愿放在浏览器做为loader也可以放在服务端,提供加载功能。在我看来,AMD擅长在浏览器端、CMD擅长在服务器端。这是由于浏览器加载一个功能不像服务器那么快,有大量的网络消耗。因此一个异步loader是更接地气的。浏览器

或者,干脆使用YUI3的模块机制,在上线前进行压制。把互相依赖的模块压在一个文件中。服务器

---------------------------------------------------------------------------------------------------

 

每个卓越的思想都有一份朴实的代码实现。因此不管AMD与CMD都要面临如下几个问题:

  一、模块式如何注册的,define函数都作了什么?
  二、他们是如何知道模块的依赖?
  三、如何作到异步加载?尤为是seajs如何作到异步加载延迟执行的?
  辩证法第一规律:事物之间具备有机联系。AMD与CMD都借鉴了CommonJs,宏观层面必有一致性,好比总体处理流程:
  模块的加载解析到执行过程一共经历了6个步骤:
  一、由入口进入程序
  二、进入程序后首先要作的就是创建一个模块仓库(这是防止重复加载模块的关键),JavaScript原生的object对象最为适合,key表明模块Id,value表明各个模块,处理主模块
  三、向模块仓库注册一模块,一个模块最少包含四个属性:id(惟一标识符)、deps(依赖项的id数组)、factory(模块自身代码)、status(模块的状态:未加载、已加载未执行、已执行等),放到代码中固然仍是object最合适
  四、模块便是JavaScript文件,使用无阻塞方式(动态建立script标签)加载模块
scriptElement= document.createElement('script');
scriptElement.src = moduleUrl;
scriptElement.async = true;
scriptElement.onload = function(){.........};
document.head.appendChild(scriptElement);

  五、模块加载完毕后,获取依赖项(amd、cmd区别),改变模块status,由statuschange后,检测全部模块的依赖项。

  因为requirejs与seajs遵循规范不一样,requirejs在define函数中能够很容易得到当前模块依赖项。而seajs中不须要依赖声明,因此必须作一些特殊处理才可否得到依赖项。方法将factory做toString处理,而后用正则匹配出其中的依赖项,好比出现require(./a),则检测到须要依赖a模块。

  同时知足非阻塞和顺序执行就须要须要对代码进行一些预处理,这是因为CMD规范和浏览器环境特色所决定的。

  六、若是模块的依赖项彻底加载完毕(amd中须要执行完毕,cmd中只须要文件加载完毕,注意这时候的factory还没有执行,当使用require请求该模块时,factory才会执行,因此在性能上seajs逊于requirejs),执行主模块的factory函数;不然进入步骤3.

 

  AMD规范定义了一个自由变量或者说是全局变量 define 的函数

define( id?, dependencies?, factory );    

 

    第一个参数 id 为字符串类型,表示了模块标识,为可选参数。若不存在则模块标识应该默认定义为在加载器中被请求脚本的标识。若是存在,那么模块标识必须为顶层的或者一个绝对的标识。
    第二个参数,dependencies ,是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。
    第三个参数,factory,是一个须要进行实例化的函数或者一个对象。
建立模块标识为 alpha 的模块,依赖于 require, export,和标识为 beta 的模块  
define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){
    export.verb = function(){
        return beta.verb();
        // or:
        return require("beta").verb();
    }
});
    
一个返回对象字面量的异步模块
define(["alpha"], function( alpha ){
    return {
        verb : function(){
            return alpha.verb() + 1 ;
        }
    }
});

 

    无依赖模块能够直接使用对象字面量来定义
define( {
    add : function( x, y ){
        return x + y ;
    }
} );

 

require();
在 AMD 规范中的 require 函数与通常的 CommonJS中的 require 不一样。因为动态检测依赖关系使加载异步,对于基于回调的 require 需求强烈。

    局部 与 全局 的require

    局部的 require 须要在AMD模式中的 define 工厂函数中传入 require。
define( ['require'], function( require ){
  // ...
} );
or:
define( function( require, exports, module ){
  // ...
} );

 

    局部的 require 须要其余特定的 API 来实现。
    全局的 require 函数是惟一全局做用域下的变量,像 define同样。全局的 require 并非规范要求的,可是若是实现全局的 require函数,那么其须要具备与局部 require 函数 同样的如下的限定:
    1. 模块标识视为绝对的,而不是相对的对应另外一个模块标识。
    2. 只有在异步状况下,require的回调方式才被用来做为交互操做使用。由于他不可能在同步的状况下经过 require(String) 从顶层加载模块。
    依赖相关的API会开始模块加载。若是须要有互操做的多个加载器,那么全局的 reqiure 应该被加载顶层模块来代替。
require(String)
define( function( require ){
    var a = require('a'); // 加载模块a
} );
require(Array, Function)
define( function( require ){
    require( ['a', 'b'], function( a,b ){ // 加载模块a b 使用
        // 依赖 a b 模块的运行代码
    } ); 
} );
require.toUrl( Url )
define( function( require ){
    var temp = require.toUrl('./temp/a.html'); // 加载页面
} );

 

define 和 require 这两个定义模块,调用模块的方法合称为AMD模式,定义模块清晰,不会污染全局变量,清楚的显示依赖关系。AMD模式能够用于浏览器环境而且容许非同步加载模块,也能够按需动态加载模块。
 
在CMD中,一个模块就是一个文件,格式为:
    define( factory );
全局函数define,用来定义模块。
    参数 factory  能够是一个函数,也能够为对象或者字符串。
    当 factory 为对象、字符串时,表示模块的接口就是该对象、字符串。
    定义JSON数据模块:
define({ "foo": "bar" });

 

    经过字符串定义模板模块:
define('this is {{data}}.');

 

    factory 为函数的时候,表示模块的构造方法,执行构造方法即可以获得模块向外提供的接口。
define( function(require, exports, module) { 

    // 模块代码

});

 

define( id?, deps?, factory );
    define也能够接受两个以上的参数,字符串id为模块标识,数组deps为模块依赖:
define( 'module', ['module1', 'module2'], function( require, exports, module ){
    // 模块代码
} );

 

    其与 AMD 规范用法不一样。
require 是 factory 的第一个参数。
    require( id );
    接受模块标识做为惟一的参数,用来获取其余模块提供的接口:
define(function( require, exports ){
    var a = require('./a');
    a.doSomething();
});

 

    require.async( id, callback? );
    require是同步往下执行的,须要的异步加载模块可使用 require.async 来进行加载:
define( function(require, exports, module) { 
    require.async('.a', function(a){
        a.doSomething();
    });
});

 

    require.resolve( id )
    可使用模块内部的路径机制来返回模块路径,不会加载模块。
    exports 是 factory 的第二个参数,用来向外提供模块接口。
define(function( require, exports ){
    exports.foo = 'bar'; // 向外提供的属性
    exports.do = function(){}; // 向外提供的方法
});

 

    固然也可使用 return 直接向外提供接口。
define(function( require, exports ){
    return{
        foo : 'bar', // 向外提供的属性
        do : function(){} // 向外提供的方法
    }
});

 

    也能够简化为直接对象字面量的形式:
define({
    foo : 'bar', // 向外提供的属性
    do : function(){} // 向外提供的方法
});

 

与nodeJS中同样须要注意的是,一下方式是错误的:
define(function( require, exports ){
    exports = {
        foo : 'bar', // 向外提供的属性
        do : function(){} // 向外提供的方法
    }
});
须要这么作
define(function( require, exports, module ){
    module.exports = {
        foo : 'bar', // 向外提供的属性
        do : function(){} // 向外提供的方法
    }
});

 

   传入的对象引用能够添加属性,一旦赋值一个新的对象,那么值钱传递进来的对象引用就会失效了。开始之初,exports 是做为 module.exports 的一个引用存在,一切行为只有在这个引用上 factory 才得以正常运行,赋值新的对象后就会断开引用,exports就只是一个新的对象引用,对于factory来讲毫无心义,就会出错。
    module 是factory的第三个参数,为一个对象,上面存储了一些与当前模块相关联的属性与方法。
        module.id 为模块的惟一标识。
        module.uri 根据模块系统的路径解析规则获得模块的绝对路径。
        module.dependencies 表示模块的依赖。
        module.exports 当前模块对外提供的接口。

 

 

CommonJS
这种方式经过一个叫作require的方法,同步加载依赖,而后返导出API供其它模块使用,一个模块能够经过exports或者module.exports导出API。CommonJS规范中,一个单独的文件就是一个模块。每个模块都是一个单独的做用域,在一个文件中定义的变量,都是私有的,对其余文件是不可见的。
Well
服务端模块能够很好的复用
这种风格的模块已经不少了,好比npm上基本上都是这种风格的module
简单易用
Less Well
加载模块是同步的,因此只有加载完成才能执行后面的操做
多个模块不能并行加载
像Node.js主要用于服务器的编程,加载的模块文件通常都已经存在本地硬盘,因此加载起来比较快,不用考虑异步加载的方式,因此CommonJS规范比较适用。但若是是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。因此就有了 AMD 、CMD 的解决方案。

CommonJS规范

 CommonJS是在浏览器环境以外构建JavaScript生态系统为目标产生的项目,好比服务器和桌面环境中。CommonJS规范是为了解决JavaScript的做用域问题而定义的模块形式,
可使每一个模块在它自身的命名空间中执行。该规范的主要内容是:模块必须经过  module.exports导出对外的变量或接口,经过require()来导入其余模块的输出到当前模块。
例子:
 
// moduleA.js  
module.exports = function( value ){  
    return value * 2;  
} 
// moduleB.js  
var multiplyBy2 = require('./moduleA');  
var result = multiplyBy2(4);  

 

 
CommonJS是同步加载模块,但其实也有浏览器端的实现,其原理是将全部模块都定义好并经过id进行索引,这样就能够浏览器进行解析了
 服务器端的Node.js遵循CommonJS规范。核心思想是容许模块经过require 方法来同步加载所要依赖的其余模块,而后经过 exports或module.exports来导出须要暴露的接口。
 
require("module");  
require("../file.js");  
exports.doStuff = function() {};  
module.exports = someValue;  
优势:
  •  服务器端便于重用
  • NPM中已经将近20w个模块包
  • 简单并容易使用
缺点:
  • 同步的模块方式不适合不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
  • 不能非阻塞的并行加载多个模块

AMD

AMD规范其实只有一个主要接口 define(id,dependencies,factory),它要在声明模块的时候指定全部的依赖dependencies,而且还要当作形参传到factory中,对于依赖的模块提早执行,依赖前置
define("module", ["dep1", "dep2"], function(d1, d2) {  
  return someExportedValue;  
});  
require(["module", "../file"], function(module, file) { /* ... */ });  
优势:
  • 适合在浏览器环境异步加载
  • 并行加载多个模块
缺点:
  • 提升开发成本,代码阅读和书写比较困难
  • 不符合通用的模块思惟方式,是一种妥协的实现

CMD

CMD规范和AMD类似,尽可能保持简单,而且与CommonJS和NodeJS的Modules规范保持了很大的兼容性。
 
define(function(require, exports, module) {  
  var $ = require('jquery');  
  var Spinning = require('./spinning');  
  exports.doSomething = ...  
  module.exports = ...  
})  

 

优势:
  • 依赖就近,延迟执行
  • 很容易在node中运行
缺点:
  • 依赖SPM打包,模块的加载逻辑偏重

转载:https://www.zhihu.com/question/20351507

http://blog.csdn.net/vuturn/article/details/51970567

相关文章
相关标签/搜索