AMD, CMD, CommonJS和UMD

個人github(PS:但願star): https://github.com/tonyzheng1...javascript

今天因爲項目中引入的echarts的文件太大,requirejs常常加載超時,不得不分開來加載echarts的各個圖表。可是使用echarts自帶的在線構建工具生成的支持AMD 標準的模塊報錯,因此不得不使用echarts的全局函數,使用requirejs的shim進行加載。藉此機會學習一下AMD, CMD, CommonJS和UMD各自的規範,和它們之間的區別。html

Javascript模塊化

在瞭解這些規範以前,仍是先了解一下什麼是模塊化。前端

模塊化是指在解決某一個複雜問題或者一系列的雜糅問題時,依照一種分類的思惟把問題進行系統性的分解以之處理。模塊化是一種處理複雜系統分解爲代碼結構更合理,可維護性更高的可管理的模塊的方式。能夠想象一個巨大的系統代碼,被整合優化分割成邏輯性很強的模塊時,對於軟件是一種何等意義的存在。對於軟件行業來講:解耦軟件系統的複雜性,使得無論多麼大的系統,也能夠將管理,開發,維護變得「有理可循」。java

還有一些對於模塊化一些專業的定義爲:模塊化是軟件系統的屬性,這個系統被分解爲一組高內聚,低耦合的模塊。那麼在理想狀態下咱們只須要完成本身部分的核心業務邏輯代碼,其餘方面的依賴能夠經過直接加載被人已經寫好模塊進行使用便可。git

首先,既然是模塊化設計,那麼做爲一個模塊化系統所必須的能力:github

  1. 定義封裝的模塊。編程

  2. 定義新模塊對其餘模塊的依賴。api

  3. 可對其餘模塊的引入支持。數組

好了,思想有了,那麼總要有點什麼來創建一個模塊化的規範制度吧,否則各式各樣的模塊加載方式只會將局攪得更爲混亂。那麼在JavaScript中出現了一些非傳統模塊開發方式的規範 CommonJS的模塊規範,AMD(Asynchronous Module Definition),CMD(Common Module Definition)等。瀏覽器

CommonJS

CommonJS是服務器端模塊的規範,Node.js採用了這個規範。

根據CommonJS規範,一個單獨的文件就是一個模塊。加載模塊使用require方法,該方法讀取一個文件並執行,最後返回文件內部的exports對象。

例如:

// foobar.js
 
//私有變量
var test = 123;
 
//公有方法
function foobar () {
 
    this.foo = function () {
        // do someing ...
    }
    this.bar = function () {
        //do someing ...
    }
}
 
//exports對象上的方法和變量是公有的
var foobar = new foobar();
exports.foobar = foobar;
//require方法默認讀取js文件,因此能夠省略js後綴
var test = require('./boobar').foobar;
 
test.bar();

CommonJS 加載模塊是同步的,因此只有加載完成才能執行後面的操做。像Node.js主要用於服務器的編程,加載的模塊文件通常都已經存在本地硬盤,因此加載起來比較快,不用考慮異步加載的方式,因此CommonJS規範比較適用。但若是是瀏覽器環境,要從服務器加載模塊,這是就必須採用異步模式。因此就有了 AMD CMD 解決方案。

AMD和RequireJS

AMD

AMD是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義".

AMD設計出一個簡潔的寫模塊API:
define(id?, dependencies?, factory);
第一個參數 id 爲字符串類型,表示了模塊標識,爲可選參數。若不存在則模塊標識應該默認定義爲在加載器中被請求腳本的標識。若是存在,那麼模塊標識必須爲頂層的或者一個絕對的標識。
第二個參數,dependencies ,是一個當前模塊依賴的,已被模塊定義的模塊標識的數組字面量。
第三個參數,factory,是一個須要進行實例化的函數或者一個對象。

經過參數的排列組合,這個簡單的API能夠從容應對各類各樣的應用場景,以下所述。

  • 定義無依賴的模塊

define( {
    add : function( x, y ){
        return x + y ;
    }
} );
  • 定義有依賴的模塊

define(["alpha"], function( alpha ){
    return {
        verb : function(){
            return alpha.verb() + 1 ;
        }
    }
});
  • 定義數據對象模塊

define({
    users: [],
    members: []
});
  • 具名模塊

define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){
    export.verb = function(){
        return beta.verb();
        // or:
        return require("beta").verb();
    }
});
  • 包裝模塊

define(function(require, exports, module) {
    var a = require('a'),
          b = require('b');

    exports.action = function() {};
} );

不考慮多了一層函數外,格式和Node.js是同樣的:使用require獲取依賴模塊,使用exports導出API。

除了define外,AMD還保留一個關鍵字require。require 做爲規範保留的全局標識符,能夠實現爲 module loader,也能夠不實現。

模塊加載

require([module], callback)

AMD模塊化規範中使用全局或局部的require函數實現加載一個或多個模塊,全部模塊加載完成以後的回調函數。

其中:

[module]:是一個數組,裏面的成員就是要加載的模塊;
callback:是模塊加載完成以後的回調函數。

例如:加載一個math模塊,而後調用方法 math.add(2, 3);

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

RequireJS

RequireJS 是一個前端的模塊化管理的工具庫,遵循AMD規範,它的做者就是AMD規範的創始人 James Burke。因此說RequireJS是對AMD規範的闡述一點也不爲過。

RequireJS 的基本思想爲:經過一個函數來將全部所須要的或者說所依賴的模塊實現裝載進來,而後返回一個新的函數(模塊),咱們全部的關於新模塊的業務代碼都在這個函數內部操做,其內部也可無限制的使用已經加載進來的以來的模塊。

<script data-main='scripts/main' src='scripts/require.js'></script>

那麼scripts下的main.js則是指定的主代碼腳本文件,全部的依賴模塊代碼文件都將從該文件開始異步加載進入執行。

define用於定義模塊,RequireJS要求每一個模塊均放在獨立的文件之中。按照是否有依賴其餘模塊的狀況分爲獨立模塊和非獨立模塊。

  • 獨立模塊,不依賴其餘模塊。直接定義:

define({
    method1: function(){},
    method2: function(){}
});

也等價於

define(function() {
    return {
        method1: function(){},
        method2: function(){}
    }
});
  • 非獨立模塊,對其餘模塊有依賴。

define([ 'module1', 'module2' ], function(m1, m2) {
    ...
});

或者:

define(function(require) {
    var m1 = require('module1'),
          m2 = require('module2');
    ...
});

簡單看了一下RequireJS的實現方式,其 require 實現只不過是提取 require 以後的模塊名,將其放入依賴關係之中。

  • require方法調用模塊

在require進行調用模塊時,其參數與define相似。

require(['foo', 'bar'], function(foo, bar) {
    foo.func();
    bar.func();
} );

在加載 foo 與 bar 兩個模塊以後執行回調函數實現具體過程。

固然還能夠如以前的例子中的,在define定義模塊內部進行require調用模塊

define(function(require) {
    var m1 = require( 'module1' ),
          m2 = require( 'module2' );
    ...
});

define 和 require 這兩個定義模塊,調用模塊的方法合稱爲AMD模式,定義模塊清晰,不會污染全局變量,清楚的顯示依賴關係。AMD模式能夠用於瀏覽器環境而且容許非同步加載模塊,也能夠按需動態加載模塊。

官網 (http://www.requirejs.org/)
API (http://www.requirejs.org/docs...

CMD和SeaJS

CMD是SeaJS 在推廣過程當中對模塊定義的規範化產出

  • 對於依賴的模塊AMD是提早執行,CMD是延遲執行。不過RequireJS從2.0開始,也改爲能夠延遲執行(根據寫法不一樣,處理方式不經過)。

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

//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();
    }
});

雖然 AMD也支持CMD寫法,但依賴前置是官方文檔的默認模塊定義寫法。

  • AMD的API默認是一個當多個用,CMD嚴格的區分推崇職責單一。例如:AMD裏require分全局的和局部的。CMD裏面沒有全局的 require,提供 seajs.use()來實現模塊系統的加載啓動。CMD裏每一個API都簡單純粹。

UMD

UMD是AMD和CommonJS的糅合

AMD模塊以瀏覽器第一的原則發展,異步加載模塊。
CommonJS模塊以服務器第一原則發展,選擇同步加載,它的模塊無需包裝(unwrapped modules)。
這迫令人們又想出另外一個更通用的模式UMD (Universal Module Definition)。但願解決跨平臺的解決方案。

UMD先判斷是否支持Node.js的模塊(exports)是否存在,存在則使用Node.js模塊模式。
在判斷是否支持AMD(define是否存在),存在則使用AMD方式加載模塊。

(function (window, factory) {
    if (typeof exports === 'object') {
     
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
     
        define(factory);
    } else {
     
        window.eventUtil = factory();
    }
})(this, function () {
    //module ...
});
相關文章
相關標籤/搜索