AMD規範與CMD規範的區別

龍恩0707發表於2014-01-06

AMD規範與CMD規範的區別是什麼?

   在比較之前,我們得先來了解下什麼是AMD規範?什麼是CMD規範?當然先申明一下,我個人也是總結下而已,也是網上看到的資料,自己總結下或者可以說整理下而已,供大家更深入的瞭解!因為我們都知道 AMD規範:是 RequireJS 在推廣過程中對模組定義的規範化產出的,而CMD規範:是SeaJS 在推廣過程中對模組定義的規範化產出的。

什麼是CMD規範?

  在CMD中 一個模組就是一個檔案,如下程式碼所示:

//基本格式如:define(id, deps, factory)
// 比如如下程式碼
define('hello',['jQuery'],function(require, exports, module) {
  // 模組程式碼
});
define是一個全域性函式,主要是用來定於模組的。其中如上'hello'就是模組名稱,['jquery']是依賴項,也可以依賴於多項可以如下寫法['jquery','',''],分別用逗號隔開,其中上面的id(模組名稱)和deps(被依賴項) 是可以省略的。省略時,那麼模組名稱就是檔名稱。比如我有個檔案叫a.js,那麼我定義模組時候如下程式碼所示:
define(function(require, exports, module) {
  // 模組程式碼
});
那麼如果我想在b.js程式碼裡面要依賴於a.js的話,那麼我可以直接這樣寫:
define(function(require,exports,module){ var a = require('a')});
但是注意:帶有id 和 deps 是不屬於CMD規範的。所以在seaJS裡面 一般的寫法是不帶模組名稱和依賴項的。就是如上的程式碼格式。
下面看看 factory 在seajs裡面 factory既可以是函式,物件或者字串。factory為物件 字串時候,表示該模組的介面就是該物件或者字串,如下可以定義一個json物件。
define({"aa":'bb'});和jsonp格式類似,不過這樣的資料物件是高度可用的,而且因為是靜態物件,他也是CDN友好的,可以提高效能,比如說我們 有個省市區這麼一個jSON格式要返回我們前端,如果以傳統JSONP的形式提供給客戶端,它必須提供一個callback函式名,根據這個函式名動態生成返回資料,這使得標準JSONP資料一定不是CDN友好的。那麼我們可以利用這種格式
define({  
    provinces: [
    {
        name: '上海', 
        areas: ['浦東新區', '徐彙區']},
    {
        name: '江蘇', 
        cities: ['南京', '南通']}
        //.....  
    ]
});
假設這個檔名為china.js,那麼如果某個模組需要這個資料,只需要:
define(function(require,exports,module){
var china = require('./china'); //在這裡使用中國省市資料 });
當factory為函式時,表示該模組的構造方法,執行該構造方法,可以得到模組向外提供的介面。預設會傳入三個引數require,exports,module.那麼我們先來看看require引數吧!
require:
 require是一個方法,他可以解決依賴,用於獲取其他模組提供的介面,比如下面的a.js程式碼如下

  define(function(require, exports) {
    exports.a = function(){
    // 很多程式碼
    };
  });

   那麼現在我想在b.js裡面呼叫a.js裡面的a方法。我們可以如下做:

   define(function(require,exports){

        var fun = require('./a');

    console.log(fun.a()); // 就可以呼叫到及執行a函式了。

   })

  require.async(id,callback) :   require.async: 方法用來在模組內部非同步載入模組,並在載入完成後執行指定回撥。callback引數可選。比如如下程式碼:
define(function(require, exports, module) {

  // 非同步載入一個模組,在載入完成時,執行回撥
  require.async('./b', function(b) {
    b.doSomething();
  });

  // 非同步載入多個模組,在載入完成時,執行回撥
  require.async(['./c', './d'], function(c, d) {
    c.doSomething();
    d.doSomething();
  });

});

注意: require是同步往下執行的,而require.async 則是非同步回撥執行。

 require.resolve(id)

使用模組系統內部的路徑解析機制來解析並返回模組路徑。該函式不會載入模組,只返回解析後的絕對路徑。
define(function(require, exports) {

  console.log(require.resolve('./b'));
  // ==> http://example.com/path/to/b.js

});

exports

 exports:是一個物件,用來向外提供模組介面。
比如還是上面的程式碼:如下a.js裡面
define(function(require, exports) {
    exports.a = function(){
        // 很多程式碼    
    };
});

 或者如下書寫:

define(function(require, exports) {
    return {
              i: 'a',
              a: function(){ 
                   // 執行相應的程式碼
              }
        }
});

那麼如果我在b.js裡面想要呼叫a.js裡面的a方法,由於a.js使用exports對外提供了介面a方法,那麼在b.js裡面 我們只需要先這樣 var fun = require('./a');然後執行fun.a();就可以執行a方法了。

module

module 是一個物件,上面儲存了與當前模組相關聯的一些屬性和方法。其中exports是module.exports的一個引用。
moudle.id
 模組的唯一標識。如下程式碼:
define('id', [], function(require, exports, module) {
  // 模組程式碼
});
module.uri
根據模組系統的路徑解析規則得到的模組絕對路徑。
define(function(require, exports, module) {
  console.log(module.uri); 
  // ==> http://example.com/path/to/this/file.js
});
等等屬性 具體可以看seajs官網。原理是一樣的 因為seajs也採用的是CMD規範。

  什麼是AMD規範?

如下官網程式碼:
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
    exports.verb = function() {
        return beta.verb();
        //或者:
        return require("beta").verb();
    }
});

  這裡的require函式讓你能夠隨時去依賴一個模組,即取得模組的引用,從而即使模組沒有作為引數定義,也能夠被使用;exports是定義的 alpha 模組的實體,在其上定義的任何屬性和方法也就是alpha模組的屬性和方法。通過exports.verb = ...就是為alpha模組定義了一個verb方法。例子中是簡單呼叫了模組beta的verb方法。

其中上面的模組名稱和依賴項 也可以省略,那麼如果省略的話,那麼我們可以稱他們為匿名函式,那麼同樣 模組名稱就是檔名稱 和上面的CMD規範類似。上面的['',''] 引數代表了一組對所定義的模組來說必須的依賴項。第三個引數('definition function')是一個用來為你的模組執行初始化的函式。一個最簡單的模組可以以如下方式定義:
// 模組定義函式 
// 依賴項(foo 和 bar)被對映為函式的引數
define('myMoudle',['foo','bar'],function(foo,bar){
    // 返回一個定義了模組匯出介面的值
    // (也就是我們想要匯出後進行呼叫的功能)
    // 在這裡建立模組
    var myModule = {
        doStuff:function(){
            console.log('Yay! Stuff');
        }
    }
    return myModule;
});

還可以這樣書寫:

// 另一個例子可以是...
define('myModule',['math', 'graph'], function ( math, graph ) {

        // 請注意這是一個和 AMD 有些許不同的模式,但用幾種不同的方式
        // 來定義模組也是可以的,因為語法在某些方面還是比較靈活的
        return {
            plot: function(x, y){
                return graph.drawPie(math.randomGrid(x,y));
            }
        }
    };
});

另一方面,require 則主要用來在頂層 JavaScript 檔案中或須要動態讀取依賴時載入程式碼。用法的一個例項如下:

// 假設 'foo' 和 'bar' 是兩個外部模組
// 在本例中,這兩個模組被載入後的 'exports' 被當做兩個引數傳遞到了回撥函式中
// 所以可以像這樣來訪問他們
require(['foo', 'bar'], function ( foo, bar ) {
        // 這裡寫其餘的程式碼
        foo.doSomething();
});

動態載入的依賴項 如下程式碼:

define(function ( require ) {
    var isReady = false, 
        foobar;
 
    // 請注意在模組定義內部內聯的 require 語句
    require(['foo', 'bar'], function (foo, bar) {
        isReady = true;
        foobar = foo() + bar();
    });
 
    // 我們仍可以返回一個模組
    return {
        isReady: isReady,
        foobar: foobar
    };
});

 CMD規範與AMD規範的區別如下:

 1. CMD依賴就近:比如如下程式碼

define(function(require,exports,module){  
        var a = require('./a');  
        a.doSomthing();  
});

   程式碼在執行時,首先是不知道依賴的,需要遍歷所有的require關鍵字,找出後面的依賴。具體做法是將function toString後,用正則匹配出require關鍵字後面的依賴。

   而AMD依賴前置:如下程式碼:

define(['./a','./b'],function(a,b){  
       //......  
       a.doSomthing();  
       //......  
       b.doSomthing();  
})  

程式碼在一旦執行到此處,能立即知曉依賴。而無需遍歷整個函式體找到它的依賴,因此效能有所提升,缺點就是開發者必須顯式得指明依賴——這會使得開發工作量變大,比如:當依賴項有n個時候 那麼寫起來比較煩 且容易出錯。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。

2. 執行順序上:

  CMD是延遲執行的,而AMD是提前執行的。

3. api設計角度

  AMD 的 API 預設是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。比如 AMD 裡,require 分全域性 require 和區域性 require,都叫 require。CMD 裡,沒有全域性 require,而是根據模組系統的完備性,提供 seajs.use 來實現模組系統的載入啟動。CMD 裡,每個 API 都簡單純粹

相關文章