ES6模塊化開發及AMD、CMD、Require.js、sea.js、common.js、ES6的對比:

一,模塊化開發:

  1. 定義:

    所謂的模塊化開發就是封裝細節,提供使用接口,彼此之間互不影響,每個模塊都是實現某一特定的功能。模塊化開發的基礎就是函數

  2. 使用函數封裝:

    function func1(){
        //...
    }
    function func2(){
        //...
    }
    

    注釋 :

    上面的函數func1 ()和func2 (),組成一個模塊。使用的時候,直接調用就行了。這種做法的缺點很明顯:影響了全局變量,無法保證不與其他模塊發生變量名沖突,而且模塊成員之間看不出直接關系。

  3. 立即執行函數的寫法:

    使用"立即執行函數"(Immediately-Invoked FunctionExpression,IIFE),可以達到不暴露私有成員的目的。這個也是閉包處理的一種方式

    var obj= (function(){
        var _age= 0;
        var func1= function(){
          //...
        };
        var func2= function(){
          //...
        };
        return {
          m1 : func1,
          m2 : func2
        };
    })();
    

    使用上面的寫法,外部代碼無法讀取內部的age變量。

    console.info(obj.age);//undefined
    
  4. 放大模式:

    如果一個模塊很大,必須分成幾個部分,或者一個模塊需要繼承另一個模塊,這時就有必要采用"放大模式"(augmentation)。在原有的基礎上擴展更多的方法。

    var obj =(function (mod){
        mod.func3 = function () {
          //...
        };
        return mod;//方便方法連續調用
    })(obj);
    

    上面的代碼為obj模塊添加了一個新方法func3 (),然后返回新的obj模塊,方便方法連續調用。如何防止obj為null或undefined的情況? 可以采用寬放大模式。

5.寬放大模式(Loose augmentation):

? 在瀏覽器環境中,模塊的各個部分通常都是從網上獲取的,有時無法知道哪個部分會先加載。如果采用上面的寫法,第一個執行的部 分有可能加載一個不存在的空對象,這時就要采用"寬放大模式"。

var obj =( function (mod){
    //...
    return mod;
  })(window.obj|| {});//確保對象不為空

6.輸入全局變量:

? 獨立性是模塊的重要特點,模塊內部最好不與程序的其他部分直接交互。為了在模塊內部調用全局變量,必須顯式地將其他變量輸入模塊。

(function(window, undefined ) {
……
})(window);

這是jQuery框架的源碼,將window對象作為參數傳入,這樣做除了保證模塊的獨立性,還使得模塊之間的依賴關系變得明顯。

目前,通行的JavaScript模塊規范共有兩種:CommonJS和AMD。

二,AMD、CMD、Require.js、sea.js、common.js、ES6的對比:

他們都是用于在模塊化定義中使用的,AMD、CMD、CommonJs是ES5中提供的模塊化編程的方案,import/export是ES6中定義新增的

  1. AMD-異步模塊定義:

    AMD是RequireJS在推廣過程中對模塊定義的規范化產出,它是一個概念,RequireJS是對這個概念的實現,就好比JavaScript語言是對ECMAScript規范的實現。AMD是一個組織,RequireJS是在這個組織下自定義的一套腳本語言

    define(['package/lib'], function(lib){ 
      function foo(){
        lib.log('hello');
        return {
            foo: foo
        };
    });
    

    RequireJS:是一個AMD框架,可以異步加載JS文件,按照模塊加載方法,通過define()函數定義,第一個參數是一個數組,里面定義一些需要依賴的包,第二個參數是一個回調函數,通過變量來引用模塊里面的方法,最后通過return來輸出。

    :RequireJS是一個依賴前置、異步定義的AMD框架(在參數里面引入js文件),在定義的同時如果需要用到別的模塊,在最前面定義好即在參數數組里面進行引入,在回調里面加載

  2. CMD-同步模塊定義:

    CMD是SeaJS在推廣過程中對模塊定義的規范化產出,是一個同步模塊定義,是SeaJS的一個標準,SeaJS是CMD概念的一個實現,SeaJS是淘寶團隊提供的一個模塊開發的js框架

    define(function(require,exports,module){
      //通過require 引入依賴(依賴就近原則)
      var $ = require('jquery');
      var Spinning = require('./spinning');
    }
    

    通過define()定義,沒有依賴前置,通過require加載jQuery插件,CMD是依賴就近,在什么地方使用到插件就在什么地方require該插件,即用即返,這是一個同步的概念

  3. Require.js規范:

    RequireJS是一個工具庫,主要用于客戶端的模塊管理。它可以讓客戶端的代碼分成一個個模塊,實現異步或動態加載,從而提高代碼的性能和可維護性。它的模塊管理遵守AMD規范(Asynchronous Module Definition)。

    RequireJS的基本思想是,通過define方法,將代碼定義為模塊;通過require方法,實現代碼的模塊加載。

    首先,將require.js嵌入網頁,然后就能在網頁中進行模塊化編程了。

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

    上面代碼的data-main屬性不可省略,用于指定主代碼所在的腳本文件,在上例中為scripts子目錄下的main.js文件。用戶自定義的代碼就放在這個main.js文件中。

    define方法:定義模塊

    define方法用于定義模塊,RequireJS要求每個模塊放在一個單獨的文件里。

    按照是否依賴其他模塊,可以分成兩種情況討論。第一種情況是定義獨立模塊,即所定義的模塊不依賴其他模塊;第二種情況是定義非獨立模塊,即所定義的模塊依賴于其他模塊。

    (1)獨立模塊

    如果被定義的模塊是一個獨立模塊,不需要依賴任何其他模塊,可以直接用define方法生成。

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

    上面代碼生成了一個擁有method1、method2兩個方法的模塊。

    另一種等價的寫法是,把對象寫成一個函數,該函數的返回值就是輸出的模塊。

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

    后一種寫法的自由度更高一點,可以在函數體內寫一些模塊初始化代碼。

    值得指出的是,define定義的模塊可以返回任何值,不限于對象。

    (2)非獨立模塊

    如果被定義的模塊需要依賴其他模塊,則define方法必須采用下面的格式。

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

    define方法的第一個參數是一個數組,它的成員是當前模塊所依賴的模塊。比如,['module1', 'module2']表示我們定義的這個新模塊依賴于module1模塊和module2模塊,只有先加載這兩個模塊,新模塊才能正常運行。一般情況下,module1模塊和module2模塊指的是,當前目錄下的module1.js文件和module2.js文件,等同于寫成['./module1', './module2']。

    define方法的第二個參數是一個函數,當前面數組的所有成員加載成功后,它將被調用。它的參數與數組的成員一一對應,比如function(m1, m2)就表示,這個函數的第一個參數m1對應module1模塊,第二個參數m2對應module2模塊。這個函數必須返回一個對象,供其他模塊調用。

    define(['module1', 'module2'], function(m1, m2) {
    
        return {
            method: function() {
                m1.methodA();
                m2.methodB();
            }
        };
    
    });
    

    上面代碼表示新模塊返回一個對象,該對象的method方法就是外部調用的接口,menthod方法內部調用了m1模塊的methodA方法和m2模塊的methodB方法。

    需要注意的是,回調函數必須返回一個對象,這個對象就是你定義的模塊。

    如果依賴的模塊很多,參數與模塊一一對應的寫法非常麻煩。

    define(
        [       'dep1', 'dep2', 'dep3', 'dep4', 'dep5', 'dep6', 'dep7', 'dep8'],
        function(dep1,   dep2,   dep3,   dep4,   dep5,   dep6,   dep7,   dep8){
            ...
        }
    );
    

    為了避免像上面代碼那樣繁瑣的寫法,RequireJS提供一種更簡單的寫法。

    define(
        function (require) {
            var dep1 = require('dep1'),
                dep2 = require('dep2'),
                dep3 = require('dep3'),
                dep4 = require('dep4'),
                dep5 = require('dep5'),
                dep6 = require('dep6'),
                dep7 = require('dep7'),
                dep8 = require('dep8');
    
                ...
        }
    
    });
    

    下面是一個define實際運用的例子。

    define(['math', 'graph'], 
        function ( math, graph ) {
            return {
                plot: function(x, y){
                    return graph.drawPie(math.randomGrid(x,y));
                }
            }
        };
    );
    

    上面代碼定義的模塊依賴math和graph兩個庫,然后返回一個具有plot接口的對象。

    另一個實際的例子是,通過判斷瀏覽器是否為IE,而選擇加載zepto或jQuery。

    define(('__proto__' in {} ? ['zepto'] : ['jquery']), function($) {
        return $;
    });
    

    上面代碼定義了一個中間模塊,該模塊先判斷瀏覽器是否支持proto屬性(除了IE,其他瀏覽器都支持),如果返回true,就加載zepto庫,否則加載jQuery庫。

    require方法:調用模塊

    require方法用于調用模塊。它的參數與define方法類似。

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

    上面方法表示加載foo和bar兩個模塊,當這兩個模塊都加載成功后,執行一個回調函數。該回調函數就用來完成具體的任務。

    require方法的第一個參數,是一個表示依賴關系的數組。這個數組可以寫得很靈活,請看下面的例子。

    require( [ window.JSON ? undefined : 'util/json2' ], function ( JSON ) {
      JSON = JSON || window.JSON;
    
      console.log( JSON.parse( '{ "JSON" : "HERE" }' ) );
    });
    

    上面代碼加載JSON模塊時,首先判斷瀏覽器是否原生支持JSON對象。如果是的,則將undefined傳入回調函數,否則加載util目錄下的json2模塊。

    require方法也可以用在define方法內部。

    define(function (require) {
       var otherModule = require('otherModule');
    });
    

    下面的例子顯示了如何動態加載模塊。

    define(function ( require ) {
        var isReady = false, foobar;
     
        require(['foo', 'bar'], function (foo, bar) {
            isReady = true;
            foobar = foo() + bar();
        });
     
        return {
            isReady: isReady,
            foobar: foobar
        };
    });
    

    上面代碼所定義的模塊,內部加載了foo和bar兩個模塊,在沒有加載完成前,isReady屬性值為false,加載完成后就變成了true。因此,可以根據isReady屬性的值,決定下一步的動作。

    下面的例子是模塊的輸出結果是一個promise對象。

    define(['lib/Deferred'], function( Deferred ){
        var defer = new Deferred(); 
        require(['lib/templates/?index.html','lib/data/?stats'],
            function( template, data ){
                defer.resolve({ template: template, data:data });
            }
        );
        return defer.promise();
    });
    

    上面代碼的define方法返回一個promise對象,可以在該對象的then方法,指定下一步的動作。

    如果服務器端采用JSONP模式,則可以直接在require中調用,方法是指定JSONP的callback參數為define。

    require( [ 
        "http://someapi.com/foo?callback=define"
    ], function (data) {
        console.log(data);
    });
    

    require方法允許添加第三個參數,即錯誤處理的回調函數。

    require(
        [ "backbone" ], 
        function ( Backbone ) {
            return Backbone.View.extend({ /* ... */ });
        }, 
        function (err) {
            // ...
        }
    );
    

    require方法的第三個參數,即處理錯誤的回調函數,接受一個error對象作為參數。

    require對象還允許指定一個全局性的Error事件的監聽函數。所有沒有被上面的方法捕獲的錯誤,都會被觸發這個監聽函數。

    requirejs.onError = function (err) {
        // ...
    };
    
  4. sea.js規范:

    • 在 Sea.js 中,所有 JavaScript 模塊都遵循 CMD(Common Module Definition) 模塊定義規范。該規范明確了模塊的基本書寫格式和基本交互規則。

      在 CMD 規范中,一個模塊就是一個文件

    • 使用seajs的步驟:

      HTML里引入seajs

      <script src="./lib/sea.js"></script>
      

      入口:seajs.use("./main");

    • 定義模塊define:

      function有三個參數:require參數用來引入別的模塊,exports和module用來導出模塊公共接口。

      define(**function**(require, exports, module) {
        *//添加jquery依賴**
      \*  **var** $ = require(**'jquery'**);
        **function** *Test*(container){
          **this**.**container** = $(container);
        }
        *//提供模塊的外部接口**
      \*  module.exports = *Test*;
        *Test*.**prototype**.changeColor= **function** () {
          **var** v = **this**.**container**;
          v.css(**'color'**,**'red'**);
        }
      });
      
  5. common.js規范:

    CommonJS規范是通過module.exports定義的,在前端瀏覽器里面并不支持module.exports,通過node.js后端使用的。Nodejs端是使用CommonJS規范的,前端瀏覽器一般使用AMD、CMD、ES6等定義模塊化開發的

    exports.area = function (r) {
      return Math.PI * r * r;
    };
    
    exports.circumference = function (r) {
      return 2 * Math.PI * r;
    };
    

    輸出方式有2種:

    • 默認輸出---module.exports
    • 帶有名字的輸出---exports.func
  6. ES6特性模塊化:

    AMD,CMD,CommoJs都是ES5里面的規范,下面的export/import是ES6里面的規范。通過export/import輸入輸出 例:

    export default {
        props:['name'],
        data () {
            return{ }
        },
        methods:{
            increment(){
                this.$emit('incre');
                import('./../until')
            },
            decrement(){
                this.$emit('decre');
            }
        }
    }
    
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。