天天看點

了解AMD ,CMD,CommonJS規範

這是一篇關于javascript子產品化AMD,CMD,CommonJS的學習總結,作為記錄也給同樣對三種方式有疑問的童鞋們,有不對或者偏差之處,望各位大神指出,不勝感激。

本篇預設讀者大概知道require,seajs的用法(AMD,CMD用法),是以沒有加入使用文法。

1、為何而生:

 這三個規範都是為javascript子產品化加載而生的,都是在用到或者預計要用到某些子產品時候加載該子產品,使得大量的系統巨大的龐雜的代碼得以很好的組織和管理。子產品化使得我們在使用和管理代碼的時候不那麼混亂,而且也友善了多人的合作。

2、那些規範們:

      (1)、CommonJS 是一個有志于建構 JavaScript 生态圈的組織。整個社群緻力于提高 JavaScript 程式的可移植性和可交換性,無論是在服務端還是浏覽器端。

     a groupwith a goal of building up the JavaScript ecosystem for web servers, desktopand command line apps and in the browser.

    一個有目标的建構JavaScript生态系統Web伺服器組,在浏覽器和指令行應用程式和桌面。(他自己wiki上這麼說的)

           這個組織呢制定了一些規範 (可以去他們網站看看 http://www.commonjs.org/)包括CommonJS Modules/1.0 規範,我們平時所說的commonjs規範,說的就是這個了。

          “TheCommonJS API will fill that gap by defining APIs that handle many commonapplication needs, ultimately providing a standard library as rich as those ofPython, Ruby and Java. ”--(出自 http://www.commonjs.org/)

    是以說Commonjs是一個更偏向于伺服器端的規範。Node.js采用了這個規範。 根據CommonJS規範,一個單獨的檔案就是一個子產品。加載子產品使用require方法,該方法讀 取一個檔案并執行,最後傳回檔案内部的exports對象。

       他又說了,可以用在下面這些場景  ,是以他更明顯的偏向伺服器端。當然你也可以把它用在浏覽器裡邊(他們自己說可以)。

·       Server-side JavaScript applications

·       Command line tools

·       Desktop GUI-based applications

·       Hybrid applications (Titanium, Adobe AIR)

  

  (2)、AMD規範

    Commonjs解決了子產品化的問題,并且可以用在浏覽器中,但是Commonjs是同步加載子產品,當要用到該子產品了,現加載現用,這種同步機制到了浏覽器裡邊就有問題了,加載速度啊啥的(覽器同步加載子產品會導緻性能、可用性、調試和跨域通路等問題)。

    鑒于浏覽器的特殊情況,又出現了一個規範,這個規範呢可以實作異步加載依賴子產品,并且會提前加載那就是AMD規範。AMD可以作為CommonJS子產品一個中轉的版本隻要CommonJS沒有被用來同步的require調用。使用同步require調用的CommonJS代碼可以被轉換為使用回調風格的AMD子產品加載器(https://github.com/amdjs/amdjs-api/wiki/AMD-(%E4%B8%AD%E6%96%87%E7%89%88) (它說的)。

    

    下面是一個使用了簡單CommonJS轉換的子產品定義(它是amd規範的一種用法):

     define(function (require, exports, module) {

       var a = require('a'),

           b = require('b');

       exports.action = function () {};

     });

    是以說AMD和Commonjs是相容的,隻要稍稍調換一下調用方法就實作了同步加載(我很懷疑amd也是在commonjs基礎上加了個殼,然後并沒有找到其他的神馬說明和支援的文字,找到了一定加到這)。

    看一下AMD規範你會發現,AMD基本都是提前說明依賴子產品,然後預加載這些子產品,實際上這就要求你提前想好這些依賴,提前寫好,不然寫代碼過程中要回到開頭繼續添加依賴。

 

  (3)、CMD

    不知道是不是針對這個問題,淘寶的玉伯大牛搞了個seajs出來,并聲稱這個規範是遵循CMD規範的,然後給出了這個規範的一個連接配接(打開會發現draft字樣)。關于這個規範呢玉伯在知乎是這麼說的

      ”AMD 是 RequireJS 在推廣過程中對子產品定義的規範化産出。

      CMD 是 SeaJS 在推廣過程中對子產品定義的規範化産出。

      類似的還有 CommonJSModules/2.0 規範,是 BravoJS 在推廣過程中對子產品定義的規範化産出。

      還有不少⋯⋯

      “

     是以這個規範實際上是為了Seajs的推廣然後搞出來的。那麼看看SeaJS是怎麼回事兒吧,基本就是知道這個規範了。

    同樣Seajs也是預加載依賴js跟AMD的規範在預加載這一點上是相同的,明顯不同的地方是調用,和聲明依賴的地方。AMD和CMD都是用difine和require,但是CMD标準傾向于在使用過程中提出依賴,就是不管代碼寫到哪突然發現需要依賴另一個子產品,那就在目前代碼用require引入就可以了,規範會幫你搞定預加載,你随便寫就可以了。但是AMD标準讓你必須提前在頭部依賴參數部分寫好(沒有寫好? 倒回去寫好咯)。這就是最明顯的差別。

3、共生共處

  由于CommonJS是伺服器端的規範,更另外兩個标準實際不沖突。

  AMD在國外用的更多,當然國内也是不少的,jQuery1,7版本開始使用,Dojo在1.6版本開始用,這已經能夠證明它足夠牛x了。

  CMD當然也有很多人在用,但是基本都集中在國内,Seajs官網就展示了一大堆牛逼的公司在用(包括愛奇藝,騰訊微網誌,支付寶,淘寶等一大堆,去這看看http://seajs.org/docs/),估計小的不出名的也不計其數了,畢竟很多公司招聘都要求會seajs嘛。

      是以三個規範目前都挺好(其實也主要是因為js麼有自己的子產品加載機制,es6出來之後不知道會怎樣)。

  當我們寫一個檔案需要相容不同的加載規範的時候怎麼辦呢,看看下面的代碼。

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

(function (root, factory) {

    if (typeof define === 'function' && define.amd) {

        // AMD

        define(['jquery', 'underscore'], factory);

    } else if (typeof exports === 'object') {

        // Node, CommonJS之類的

        module.exports = factory(require('jquery'), require('underscore'));

    } else {

        // 浏覽器全局變量(root 即 window)

        root.returnExports = factory(root.jQuery, root._);

    }

}(this, function ($, _) {

    // 方法

    function a(){}; // 私有方法,因為它沒被傳回 (見下面)

    function b(){}; // 公共方法,因為被傳回了

    function c(){}; // 公共方法,因為被傳回了

    // 暴露公共方法

    return {

        b: b,

        c: c

    }

}));   

這個代碼可以相容各種加載規範了。

  

4、AMD和CMD的差別

     下面這幾點是玉伯在知乎上說的。

  1. 對于依賴的子產品,AMD 是提前執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。CMD 推崇 as lazy aspossible.

  2. CMD 推崇依賴就近,AMD 推崇依賴前置。

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

  4. 還有一些細節差異,具體看這個規範的定義就好,就不多說了。

     ( 好吧~第四點是不多說了。。。。。。。。。)

5、AMD和CMD的一些相同

  都有difine和require,而且調用方式實際都可以添加依賴參數,也就是說都可以用提供依賴參數的方式來實作預加載依賴子產品(但是不推薦因為  注意:帶 id 和 deps 參數的 define 用法不屬于 CMD 規範,而屬于 Modules/Transport 規範。---來自:https://github.com/seajs/seajs/issues/242)。

     AMD也可以在factory中使用require來現加載用到的子產品,但是這個子產品就不會預先加載,屬于用到才加載的同步加載了。

    var a = require('a'); //加載子產品a

繼續閱讀