CommonJS—聲明模塊服務器端的API

2021-09-15 11:18 更新

CommonJS

為服務器提供的一種模塊形式的優(yōu)化

CommonJS模塊建議指定一個簡單的用于聲明模塊服務器端的API,并且不像AMD那樣嘗試去廣泛的操心諸如io,文件系統(tǒng),約定以及更多的一攬子問題。

這種形式為CommonJS所建議--它是一個把目標定在設計,原型化和標準化Javascript API的自愿者工作組。迄今為止,他們已經(jīng)在模塊和包方面做出了批復標準的嘗試。

入門

從架構的角度來看,CommonJS模塊是一個可以復用的Javascript塊,它出口對任何獨立代碼都起作用的特定對象。不同于AMD,通常沒有針對此模塊的功能封裝(因此打個比方我們并沒有在這里找到定義的相關語句)。

CommonJS模塊基本上包括兩個基礎的部分:一個取名為exports的自由變量,它包含模塊希望提供給其他模塊的對象,以及模塊所需要的可以用來引入和導出其它模塊的函數(shù)。

理解CommonJS:require()和exports

// package/lib is a dependency we require
var lib = require( "package/lib" );

// behaviour for our module
function foo(){
    lib.log( "hello world!" );
}

// export (expose) foo to other modules
exports.foo = foo;

exports的基礎使用

// define more behaviour we would like to expose
function foobar(){
  this.foo = function(){
    console.log( "Hello foo" );
  }

  this.bar = function(){
    console.log( "Hello bar" );
  }
}

// expose foobar to other modules
exports.foobar = foobar;

// an application consuming "foobar"

// access the module relative to the path
// where both usage and module files exist
// in the same directory

var foobar = require("./foobar").foobar,
    test   = new foobar();

// Outputs: "Hello bar"
test.bar();

等同于AMD的第一個CommonJS示例

define(function(require){
   var lib = require( "package/lib" );

    // some behaviour for our module
    function foo(){
        lib.log( "hello world!" );
    }

    // export (expose) foo for other modules
    return {
        foobar: foo
    };
});

這也可以用AMD支持的簡化了的CommonJS特定做到。

消耗多重依賴

app.js

var modA = require( "./foo" );
var modB = require( "./bar" );

exports.app = function(){
    console.log( "Im an application!" );
}

exports.foo = function(){
    return modA.helloWorld();
}

bar.js

exports.name = "bar";

foo.js

require( "./bar" );
exports.helloWorld = function(){
    return "Hello World!!"
}

加載器和框架對CommonJS提供了什么支持?

在瀏覽器端:

服務器端:

CommonJS適合瀏覽器么?

有開發(fā)者感覺CommonJS更適合于服務器端的開發(fā),這是如今應該用哪種形式和將要來作為面向未來的備選事實標準,在這一問題上存在一定程度分歧的原因之一。一些爭論指摘CommonJS包括許多面向服務器的特性,這些特性很容易可以看出并不能夠用Javascript在瀏覽器級別中實現(xiàn)--例如,io,系統(tǒng),而且js會被認為是借助于它們功能的性質(zhì)無法實現(xiàn)的。

那就是說,無論如何了解如何構建CommonJS模塊是有用的,那樣我們就可以更好的理解它們?nèi)绾芜m合于定義可以在任何地方使用的模塊了。模塊在客戶端和服務器端都有包括驗證,約定和模板引擎的應用程序。一些開發(fā)者趨向于選擇那種形式的方式是當一個模塊能夠在服務器端環(huán)境使用時,就選擇CommonJS,而如果不是這種場景,就使用AMD。

由于AMD模塊具有使用插件的能力,并且能夠定義更加精細的像構造器和函數(shù)之類的東西,如此是有道理的。

CommonJS模塊只能夠去定義使用起來會非常繁瑣的對象,如果我們嘗試從它們那里獲取構造器的話。

盡管這超出了本節(jié)的討論范疇,也要注意當論及AMD和CommonJS時,不同類型的“require”方法會被提到。帶有類似命名空間的問題理所當然是令人迷惑的,而社區(qū)當前對全局的require功能的優(yōu)點正存在著分歧。這里John Hann的建議是不去叫它“require”,它很可能在告知用戶關于全局的和內(nèi)部的require之間的差別,這一目標上取得失敗,將全局加載器方法重新命名為其它什么東西(例如,庫的名字)可能更加起作用。正式由于這個原因,像curl.js這樣的加載器反對使用require,而使用curl()。

AMD 與 CommonJS 存在競爭,但都是同樣有效的標準

AMD 和 CommonJS 都是有效的模塊形式,它們帶有不同的最終目標。

AMD采用瀏覽器先行的方針,它選擇了異步的行為方式,并且簡化了向后兼容性,但是它并沒有任何文件I/O的概念。它支持對象,函數(shù),構造器,字符串,JSON以及許多其它類型的模塊,在瀏覽器進行本地運行。這是令人難以置信的靈活性。

CommonJS 則在另一個方面采用了服務器端先行的方針,承載著同步行為,沒有全局的負擔并且嘗試去迎合(在服務器上的)未來。我們的意思是CommonJS支持無封裝的模塊,可以感覺到它跟ES.next/Harmony更接近一點,將我們從AMD強制使用的define()封裝中解放出來。然而CommonJS僅支持對象作為模塊。

UMD:AMD和兼容CommonJS模塊的插件

對于希望創(chuàng)建在瀏覽器和服務器端環(huán)境都能夠運作的模塊的開發(fā)者而言,現(xiàn)有的解決方案感覺可能少了點。為了有助于緩解這個問題,James Burke , 我以及許許多多其他的開發(fā)者創(chuàng)造了UMD(通用模塊定義)。

UMD是一種是實驗性質(zhì)的模塊形式,允許在編寫代碼的時候,所有或者大多數(shù)流行的實用腳本加載技術對模塊的定義在客戶端和服務器環(huán)境下都能夠起作用。另外一種模塊格式的想法盡管可能是艱巨的,出于仔細徹底的考慮,我們將簡要的概括一下UMD。最開始,我們通過簡要的看一看AMD規(guī)范中所支持的對于CommonJS的簡單封裝,來定義UMD。對于希望把模塊當做CommonJS模塊來編寫的開發(fā)者,可以應用下面的兼容CommonJS的形式:

基礎的AMD混合格式:

define( function ( require, exports, module ){

    var shuffler = require( "lib/shuffle" );

    exports.randomize = function( input ){
        return shuffler.shuffle( input );
    }
});

然而,注意到如果一個模塊并沒有包含一個依賴數(shù)組,并且定義的函數(shù)只包含最少的一個參數(shù),那么它就真的僅僅只是被當做CommonJS模塊來對待,這一點是很重要的。這在某些設備(例如PS3)上面也不會正確的工作。如需進一步了解上述的封裝,請看看:http://requirejs.org/docs/api.html#cjsmodule

進一步的考慮,我們想要提供許多不同的模式,那不僅僅只是在AMD和CommonJS上起作用,同樣也能解決開發(fā)者希望使用其它環(huán)境開發(fā)這樣的模塊時普遍遇到的問題。

下面我們可以看到這樣的變化允許我們使用CommonJS,AMD或者瀏覽全局的對象創(chuàng)建一個模塊。

使用 CommonJS,AMD或者瀏覽器全局對象創(chuàng)建模塊

定義一個模塊 commonJsStrict,它依賴于另外一個叫做B的模塊。模塊的名稱暗示了文件的名稱(,就是說一樣的),而讓文件名和導出的全局對象的名字一樣則是一種最佳實踐。

如果模塊同時也在瀏覽器中使用了相同類型的樣板,它就會創(chuàng)建一個global.b備用。如果我們不希望對瀏覽器全局補丁進行支持, 我們可以將root移除,并且把this傳遞到頂層函數(shù)作為其第一個參數(shù)。

(function ( root, factory ) {
    if ( typeof exports === 'object' ) {
        // CommonJS
        factory( exports, require('b') );
    } else if ( typeof define === 'function' && define.amd ) {
        // AMD. Register as an anonymous module.
        define( ['exports', 'b'], factory);
    } else {
        // Browser globals
        factory( (root.commonJsStrict = {}), root.b );
    }
}(this, function ( exports, b ) {
    //use b in some fashion.

    // attach properties to the exports object to define
    // the exported module properties.
    exports.action = function () {};
}));

UMD資源庫包含了在瀏覽器中能夠最優(yōu)化運作的涵蓋不同的模塊,那些對于提供導出非常不錯的,那些對于CommonJS的優(yōu)化還有那些對于定義jQuery插件作用良好的,我們會在接下里看得到。

可以在所有環(huán)境下面起作用的jQuery插件

UMD提供了兩種同jQuery一起工作的模式--一種模式定義了能夠同AMD和瀏覽器全局對象一起工作得很好的插件,而另外一種模式也能夠在CommonJS環(huán)境中起作用。jQuery并不像是能夠運行在大多數(shù)CommonJS環(huán)境中的,因此除非我們工作在一個能夠良好同jQuery一起運作的環(huán)境中,那就把這一點牢記于心。

現(xiàn)在我們將定義一個包含一個核心,以及對此核心的一個擴展的插件。核心插件被加載到一個$.core命名空間中,它可以簡單的使用借助于命名空間模式的插件擴展進行擴展。通過腳本標簽加載的插件會自動填充core下面的一個插件命名空間(比如,$core.plugin.methodName())。

這種模式操作起來相當?shù)陌?,因為插件擴展可以訪問到底層定義的屬性和方法,或者,做一些小小的調(diào)整就可以重寫行為以便它能夠被擴展來做更多的事情。加載器同樣也不在需要面面俱到了。

想要了解更多需要做的詳細信息,那就請看看下面代碼示例中內(nèi)嵌的注釋吧:

usage.html

<script type="text/javascript" src="jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="pluginCore.js"></script>
<script type="text/javascript" src="pluginExtension.js"></script>

<script type="text/javascript">

$(function(){

    // Our plugin "core" is exposed under a core namespace in
    // this example, which we first cache
    var core = $.core;

    // Then use use some of the built-in core functionality to
    // highlight all divs in the page yellow
    core.highlightAll();

    // Access the plugins (extensions) loaded into the "plugin"
    // namespace of our core module:

    // Set the first div in the page to have a green background.
    core.plugin.setGreen( "div:first");
    // Here we're making use of the core's "highlight" method
    // under the hood from a plugin loaded in after it

    // Set the last div to the "errorColor" property defined in
    // our core module/plugin. If we review the code further down,
    // we can see how easy it is to consume properties and methods
    // between the core and other plugins
    core.plugin.setRed("div:last");
});

</script>

pluginCore.js

// Module/Plugin core
// Note: the wrapper code we see around the module is what enables
// us to support multiple module formats and specifications by
// mapping the arguments defined to what a specific format expects
// to be present. Our actual module functionality is defined lower
// down, where a named module and exports are demonstrated.
//
// Note that dependencies can just as easily be declared if required
// and should work as demonstrated earlier with the AMD module examples.

(function ( name, definition ){
  var theModule = definition(),
      // this is considered "safe":
      hasDefine = typeof define === "function" && define.amd,
      // hasDefine = typeof define === "function",
      hasExports = typeof module !== "undefined" && module.exports;

  if ( hasDefine ){ // AMD Module
    define(theModule);
  } else if ( hasExports ) { // Node.js Module
    module.exports = theModule;
  } else { // Assign to common namespaces or simply the global object (window)
    ( this.jQuery || this.ender || this.$ || this)[name] = theModule;
  }
})( "core", function () {
    var module = this;
    module.plugins = [];
    module.highlightColor = "yellow";
    module.errorColor = "red";

  // define the core module here and return the public API

  // This is the highlight method used by the core highlightAll()
  // method and all of the plugins highlighting elements different
  // colors
  module.highlight = function( el,strColor ){
    if( this.jQuery ){
      jQuery(el).css( "background", strColor );
    }
  }
  return {
      highlightAll:function(){
        module.highlight("div", module.highlightColor);
      }
  };

});

pluginExtension.js

// Extension to module core

(function ( name, definition ) {
    var theModule = definition(),
        hasDefine = typeof define === "function",
        hasExports = typeof module !== "undefined" && module.exports;

    if ( hasDefine ) { // AMD Module
        define(theModule);
    } else if ( hasExports ) { // Node.js Module
        module.exports = theModule;
    } else {

        // Assign to common namespaces or simply the global object (window)
        // account for for flat-file/global module extensions
        var obj = null,
            namespaces,
            scope;

        obj = null;
        namespaces = name.split(".");
        scope = ( this.jQuery || this.ender || this.$ || this );

        for ( var i = 0; i < namespaces.length; i++ ) {
            var packageName = namespaces[i];
            if ( obj && i == namespaces.length - 1 ) {
                obj[packageName] = theModule;
            } else if ( typeof scope[packageName] === "undefined" ) {
                scope[packageName] = {};
            }
            obj = scope[packageName];
        }

    }
})( "core.plugin" , function () {

    // Define our module here and return the public API.
    // This code could be easily adapted with the core to
    // allow for methods that overwrite and extend core functionality
    // in order to expand the highlight method to do more if we wish.
    return {
        setGreen: function ( el ) {
            highlight(el, "green");
        },
        setRed: function ( el ) {
            highlight(el, errorColor);
        }
    };

});

UMD并不企圖取代AMD或者CommonJS,而僅僅只是為如今希望讓其代碼運行在更多的環(huán)境下的開發(fā)者提供一些補充的援助。想要關于這種實驗性質(zhì)的形式的更多信息或者想要貢獻建議,見:https://github.com/umdjs/umd。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號