JavaScript模塊化---AMD規范


JavaSript模塊化


    在了解AMD,CMD規范前,還是需要先來簡單地了解下什么是模塊化,模塊化開發?


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

    還有一些對於模塊化一些專業的定義為:模塊化是軟件系統的屬性,這個系統被分解為一組高內聚,低耦合的模塊。那么在理想狀態下我們只需要完成自己部分的核心業務邏輯代碼,其他方面的依賴可以通過直接加載被人已經寫好模塊進行使用即可。

首先,既然是模塊化設計,那么作為一個模塊化系統所必須的能力:
    1. 定義封裝的模塊。
    2. 定義新模塊對其他模塊的依賴。
    3. 可對其他模塊的引入支持。

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



AMD 與 RequireJS

AMD

    Asynchronous Module Definition,用白話文講就是 異步模塊定義,對於 JSer 來說,異步是再也熟悉不過的詞了,所有的模塊將被異步加載,模塊加載不影響后面語句運行。所有依賴某些模塊的語句均放置在回調函數中。

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

 



    類似與 CommonJS 方式定義

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

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

 



    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'); // 加載頁面
    } );

 



    amdjs 的API   https://github.com/amdjs/amdjs-api/wiki


RequireJS

    官網 http://www.requirejs.org/

    RequireJS 是一個前端的模塊化管理的工具庫,遵循AMD規范,它的作者就是AMD規范的創始人 James Burke。所以說RequireJS是對AMD規范的闡述一點也不為過。

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


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

 


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

    defined用於定義模塊,RequireJS要求每個模塊均放在獨立的文件之中。按照是否有依賴其他模塊的情況分為獨立模塊和非獨立模塊。
    1. 獨立模塊,不依賴其他模塊。直接定義:

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

 


    也等價於

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

 


    2. 非獨立模塊,對其他模塊有依賴。

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

 


    或者:

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

 



    簡單看了一下RequireJS的實現方式,其 require 實現只不過是將 function 字符串然后提取 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模式可以用於瀏覽器環境並且允許非同步加載模塊,也可以按需動態加載模塊。



CMD 與 seaJS

CMD

    在CMD中,一個模塊就是一個文件,格式為:
    define( factory );

    全局函數define,用來定義模塊。
    參數 factory  可以是一個函數,也可以為對象或者字符串。
    當 factory 為對象、字符串時,表示模塊的接口就是該對象、字符串。

    定義JSON數據模塊:
define({ "foo": "bar" }); 

 

 


    通過字符串定義模板模塊:

define('this is {{data}}.'); 

 


    factory 為函數的時候,表示模塊的構造方法,執行構造方法便可以得到模塊向外提供的接口。

 


    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 當前模塊對外提供的接口。


seaJS

    官網 http://seajs.org/docs/
    sea.js 核心特征:
        1. 遵循CMD規范,與NodeJS般的書寫模塊代碼。
        2. 依賴自動加載,配置清晰簡潔。
    兼容 Chrome 3+,Firefox 2+,Safari 3.2+,Opera 10+,IE 5.5+。

    seajs.use 
    用來在頁面中加載一個或者多個模塊

     // 加載一個模塊
    seajs.use('./a');
    // 加載模塊,加載完成時執行回調
    seajs.use('./a',function(a){
        a.doSomething();
    });
    // 加載多個模塊執行回調
    seajs.use(['./a','./b'],function(a , b){
        a.doSomething();
        b.doSomething();
    });

 


    其define 與 require 使用方式基本就是CMD規范中的示例。

 


AMD 與 CMD 區別到底在哪里?


    看了以上 AMD,requireJS 與 CMD, seaJS的簡單介紹會有點感覺模糊,總感覺較為相似。因為像 requireJS 其並不是只是純粹的AMD固有思想,其也是有CMD規范的思想,只不過是推薦 AMD規范方式而已, seaJS也是一樣。

    下面是玉伯對於 AMD 與 CMD 區別的解釋:

    AMD 是 RequireJS 在推廣過程中對模塊定義的規范化產出。
    CMD 是 SeaJS 在推廣過程中對模塊定義的規范化產出。

    類似的還有 CommonJS Modules/2.0 規范,是 BravoJS 在推廣過程中對模塊定義的規范化產出還有不少??

 
    這些規范的目的都是為了 JavaScript 的模塊化開發,特別是在瀏覽器端的。
    目前這些規范的實現都能達成瀏覽器端模塊化開發的目的。

 
    區別:

 
    1. 對於依賴的模塊,AMD 是提前執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。CMD 推崇 as lazy as possible.
 
    2. CMD 推崇依賴就近,AMD 推崇依賴前置。看代碼:
 
    // CMD
    define(function(require, exports, module) {
        var a = require('./a')
        a.doSomething()
        // 此處略去 100 行
        var b = require('./b') // 依賴可以就近書寫
        b.doSomething()
        // ...
    })

    // AMD 默認推薦的是
    define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好
        a.doSomething()
        // 此處略去 100 行
        b.doSomething()
        // ...
    })

 


 
雖然 AMD 也支持 CMD 的寫法,同時還支持將 require 作為依賴項傳遞,但 RequireJS 的作者默認是最喜歡上面的寫法,也是官方文檔里默認的模塊定義寫法。
 
 
    3. AMD 的 API 默認是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,沒有全局 require,而是根據模塊系統的完備性,提供 seajs.use 來實現模塊系統的加載啟動。CMD 里,每個 API 都簡單純粹。
 
 
    4. 還有一些細節差異,具體看這個規范的定義就好,就不多說了。
 
另外,SeaJS 和 RequireJS 的差異,可以參考:https://github.com/seajs/seajs/issues/277

總結

    本文主要是介紹了一下 AMD CMD的規范,順便簡單的講述了一下 requireJS 與 seaJS。講的較為籠統,下面的擴展閱讀可以更好的幫助你理解模塊化以及各個規范。


擴展閱讀:


前端模塊化開發的價值 https://github.com/seajs/seajs/issues/547
前端模塊化開發那點歷史 https://github.com/seajs/seajs/issues/588
從 CommonJS 到 Sea.js https://github.com/seajs/seajs/issues/269    

RequireJS和AMD規范  http://javascript.ruanyifeng.com/tool/requirejs.html 


知乎  AMD 和 CMD 的區別有哪些? http://www.zhihu.com/question/20351507 



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM