1. 程式人生 > >前端模塊化的一些理解-commonJs、AMD和CMD

前端模塊化的一些理解-commonJs、AMD和CMD

() urn fig comm tor 實現 ports ont 相對

---恢復內容開始---

前端模塊化規範有三種:CommonJs\AMD\CMD

CommonJs

  • 用於服務器端

AMD

  • 用於瀏覽器環境,是RequireJS在推廣過程中對模塊定義的規範化產出
  • 提前執行(異步加載:依賴先執行)+ 延遲執行

CMD

  • SeaJS在推廣過程中對模塊定義的規範化產出
  • 延遲執行(運行到需加載,根據順序執行)

模塊-實現特地功能的文件

  • 函數寫法
function f1() {
    //......  
}
function f2() {
    //......
}
  • 對象寫法

    由於函數式寫法會汙染全局變量,也可能發生變量名沖突,所以我們需要變量寫法

var module = {
    star: 0,
    f1: function() {
        //...
    },
    f2: function() {
        //...
    }
};
module.f1();
module.star = 1;

模塊寫成一個對象,模塊成員都封裝在對象裏,通過調用對象屬性,訪問使用模塊成員。但同時也暴露了模塊成員,外部可以修改模塊內部狀態

立即執行函數

var module = (function(){
    var star = 0;
    var f1 = function() {
        console.log(
"ok"); }; var f2 = function() { //... }; return { f1:f1, f2:f2 }; })(); module.f1(); //ok console.log(module.star) //undefined
  • 外部無法訪問內部私有變量

CommonJs

CommonJS是服務器端模塊的規範,由Node推廣使用。由於服務端編程的復雜性,如果沒有模塊很難與操作系統及其他應用程序互動。使用方法如下:

math.js
exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
      sum += args[i++];
    }
    return sum;
};

increment.js
var add = require(‘math‘).add;
exports.increment = function(val) {
    return add(val, 1);
};

index.js
var increment = require(‘increment‘).increment;
var a = increment(1); //2

根據CommonJS規範:

  • 一個單獨的文件就是一個模塊。每一個模塊都是一個單獨的作用域,也就是說,在該模塊內部定義的變量,無法被其他模塊讀取,除非定義為global對象的屬性。
  • 輸出模塊變量的最好方法是使用module.exports對象。

  • 加載模塊使用require方法,該方法讀取一個文件並執行,返回文件內部的module.exports對象

仔細看上面的代碼,您會註意到 require 是同步的。模塊系統需要同步讀取模塊文件內容,並編譯執行以得到模塊接口。
然而, 這在瀏覽器端問題多多。

瀏覽器端,加載 JavaScript 最佳、最容易的方式是在 document 中插入<script>標簽。但腳本標簽天生異步,傳統 CommonJS 模塊在瀏覽器環境中無法正常加載。

解決思路之一是,開發一個服務器端組件,對模塊代碼作靜態分析,將模塊與它的依賴列表一起返回給瀏覽器端。 這很好使,但需要服務器安裝額外的組件,並因此要調整一系列底層架構。

另一種解決思路是,用一套標準模板來封裝模塊定義:

define(function(require, exports, module) {

  // The module code goes here

});

這套模板代碼為模塊加載器提供了機會,使其能在模塊代碼執行之前,對模塊代碼進行靜態分析,並動態生成依賴列表。

math.js
define(function(require, exports, module) {
  exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
      sum += args[i++];
    }
    return sum;
  };
});

increment.js
define(function(require, exports, module) {
  var add = require(‘math‘).add;
  exports.increment = function(val) {
    return add(val, 1);
  };
});

index.js
define(function(require, exports, module) {
  var inc = require(‘increment‘).increment;
  inc(1); // 2
});

AMD

AMD是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義"。由於不是JavaScript原生支持,使用AMD規範進行頁面開發需要用到對應的庫函數,也就是大名鼎鼎RequireJS,實際上AMD 是 RequireJS 在推廣過程中對模塊定義的規範化的產出

它采用異步方式加載模塊,模塊的加載不影響它後面語句的運行。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之後,這個回調函數才會運行。

RequireJS主要解決兩個問題

  • 多個js文件可能有依賴關系,被依賴的文件需要早於依賴它的文件加載到瀏覽器
  • js加載的時候瀏覽器會停止頁面渲染,加載文件越多,頁面失去響應時間越長

RequireJs也采用require()語句加載模塊,但是不同於CommonJS,它要求兩個參數:

第一個參數[module],是一個數組,裏面的成員就是要加載的模塊;第二個參數callback,則是加載成功之後的回調函數。math.add()與math模塊加載不是同步的,瀏覽器不會發生假死。

require([module], callback);

require([increment‘], function (increment) {
    increment.add(1);
});
define()函數

RequireJS定義了一個函數 define,它是全局變量,用來定義模塊:
define(id?, dependencies?, factory);
參數說明:

  • id:指定義中模塊的名字,可選;如果沒有提供該參數,模塊的名字應該默認為模塊加載器請求的指定腳本的名字。如果提供了該參數,模塊名必須是“頂級”的和絕對的(不允許相對名字)。

  • 依賴dependencies:是一個當前模塊依賴的,已被模塊定義的模塊標識的數組字面量。
    依賴參數是可選的,如果忽略此參數,它應該默認為["require", "exports", "module"]。然而,如果工廠方法的長度屬性小於3,加載器會選擇以函數的長度屬性指定的參數個數調用工廠方法。

  • 工廠方法factory,模塊初始化要執行的函數或對象。如果為函數,它應該只被執行一次。如果是對象,此對象應該為模塊的輸出值。

來舉個??看看:

define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
       exports.verb = function() {
           return beta.verb();
           //Or:
           return require("beta").verb();
       }
   });
RequireJs使用例子

require.config是用來定義別名的,在paths屬性下配置別名。然後通過requirejs(參數一,參數二);參數一是數組,傳入我們需要引用的模塊名,第二個參數是個回調函數,回調函數傳入一個變量,代替剛才所引入的模塊。

main.js
//別名配置
requirejs.config({
    paths: {
        jquery: ‘jquery.min‘ //可以省略.js
    }
});
//引入模塊,用變量$表示jquery模塊
requirejs([‘jquery‘], function ($) {
    $(‘body‘).css(‘background-color‘,‘red‘);
});

引入模塊也可以只寫require()requirejs通過define()定義模塊,定義的參數上同。在此模塊內的方法和變量外部是無法訪問的,只有通過return返回才行.

math.js
define(‘math‘,[‘jquery‘], function ($) {//引入jQuery模塊
    return {
        add: function(x,y){
            return x + y;
        }
    };
});

將該模塊命名為math.js保存。

require([‘jquery‘,‘math‘], function ($,math) {
    console.log(math.add(10,100));//110
});

main.js引入模塊方法

CMD

CMD 即Common Module Definition通用模塊定義,CMD規範是國內發展出來的,就像AMD有個requireJS,CMD有個瀏覽器的實現SeaJSSeaJS要解決的問題和requireJS一樣,只不過在模塊定義方式和模塊加載(可以說運行、解析)時機上有所不同。

在 CMD 規範中,一個模塊就是一個文件。代碼的書寫格式如下:

define(function(require, exports, module) {

  // 模塊代碼

});

require是可以把其他模塊導入進來的一個參數;而exports是可以把模塊內的一些屬性和方法導出的;module 是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法。

AMD是依賴關系前置,在定義模塊的時候就要聲明其依賴的模塊;
CMD是按需加載依賴就近,只有在用到某個模塊的時候再去require:

// 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()
  ...
})
seajs使用例子
// 定義模塊  myModule.js
define(function(require, exports, module) {
  var $ = require(‘jquery.js‘)
  $(‘div‘).addClass(‘active‘);
  exports.data = 1;
});

// 加載模塊
seajs.use([‘myModule.js‘], function(my){
    var star= my.data;
    console.log(star);  //1
});

參考

這篇《前端模塊化:CommonJs,AMD和CDM》主要是個人對以下文章的總結,感謝這些老司機們的分享。
前端模塊化
詳解JavaScript模塊化開發
Javascript模塊化編程
從 CommonJS 到 Sea.js

---恢復內容結束---

前端模塊化的一些理解-commonJs、AMD和CMD