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

相關文章
相關標籤/搜索