前端模塊規範:AMD規範、CMD規範、CommonJS規範、ES6 Module
CommonJS規範
CommonJS對模塊的定義主要分爲模塊引用、模塊定義和模塊標識3個部分。
1、模塊引用
模塊引用的示例代碼如下:
var math = require('math');
在CommonJS規範中,存在require()方法,這個方法接受模塊標識,以此引入一個模塊的API到當前上下文中。
2、模塊定義
在模塊中,上下文提供require()方法來引入外部模塊。對應引入的功能,上下文提供了exports對象用於導出當前模塊的方法或者變量,並且它是唯一導出的出口。在模塊中,還存在一個module對象,它代表模塊自身,而exports是module的屬性。在Node中,一個文件就是一個模塊,將方法掛載在exports對象上作爲屬性即可定義導出的方式:
// math.js
exports.add = function () {
var sum = 0,
i = 0,
args = arguments,
l = args.length;
while (i < l) {
sum += args[i++];
}
return sum;
};
在另一個文件中,我們通過require()方法引入模塊後,就能調用定義的屬性或方法了:
// program.js
var math = require('math');
exports.increment = function (val) {
return math.add(val, 1);
};
模塊標識
模塊標識其實就是傳遞給require()方法的參數,它必須是符合小駝峯命名的字符串,或者以’.’、’..'開頭的相對路徑,或者絕對路徑。它可以沒有文件名後綴.js。
它的意義在於將類聚的方法和變量等限定在私有的作用域中,同時支持引入和導出功能以順暢地連接上下游依賴。
AMD規範
AMD規範的全稱是“Asychronous Module Definition”,即“異步模塊定義”。
它的模塊定義如下:
define(id?, dependencies?, factory);
其中模塊id和依賴是可選的,factory的內容就是實際代碼的內容。
如:
define(function() {
var exports = {};
exports.sayHello = function() {
alert('Hello from module: ' + module.id);
};
return exports;
});
和CommonJS不同之處在於AMD模塊需要define來明確定義一個模塊(Node是隱式包裝),目的是進行作用域隔離,僅在需要的時候被引入,避免掉全局變量污染和不小心被修改;
另一個區別是內容需要通過返回值的方式實現導出。
AMD規範需要再聲明模塊的時候指定所有依賴,通過形參傳到模塊內容中。
define(['dep1', 'dep2'], function (dep1, dep2) {
return function() {};
})
CDM規範
和AMD規範的主要區別在於定義模塊和依賴引入的部分。
CMD支持動態引入。
define (function(require, export , module) {
// 模塊代碼
}
require,exports, module通過形參傳遞給模塊,在需要依賴模塊時,隨時調用require()引入即可(更接近CommomJS)
define(function(require, exports) {
// 同步獲取模塊 a 的接口
var a = require('./a');
// 調用模塊 a 的方法
a.doSomething();
// 異步加載多個模塊,在加載完成時,執行回調
require.async(['./b', './c'], function(b, c) {
b.doSomething();
c.doSomething();
});
});
注意 :
require 是同步往下執行,require.async 則是異步回調執行。require.async 一般用來加載可延遲異步加載的模塊。
如何兼容多種模塊規範
以hello()方法定義到不同運行環境中,兼容Node、AMD、CMD以及常見的瀏覽器環境中:
;(function (name, definition) {
// 檢測上下文環境是否爲AMD或CMD
var hasDefine = typeof define === 'function',
// 檢查上下文環境是否爲Node
hasExports = typeof module !== 'undefined' && module.exports;
if (hasDefine) {
// AMD環境或CMD環境
define(definition);
} else if (hasExports) {
// 定義爲普通Node模塊
module.exports = definition();
} else {
// 將模塊的執行結果掛在window變量中,在瀏覽器中this指向window對象
this[name] = definition();
}
})('hello', function () {
var hello = function () {};
return hello;
});
總結
AMD和require.js
AMD是“異步模塊定義”,瀏覽器模塊開發的規範。
特點:
- 實現js文件的異步加載(避免加載時網頁失去響應);
- 聲明模塊時需要先指定依賴,推崇依賴前置,提前執行。
define(['dep1', 'dep2'], function (dep1, dep2) {
return function() {};
})
RequireJS核心原理是什麼?(如何動態加載的?如何避免多次加載的?如何緩存的?)
RequireJS是基於AMD模塊加載規範,使用回調函數來解決模塊加載問題。
動態加載原理:RequireJS是使用創建script元素, 通過指定script元素的src屬性來實現加載模塊的。
特點:
實現js文件的異步加載(避免加載時網頁失去響應);
管理模塊直接的依賴,便於代碼的編寫和維護。
CMD和sea.js
CMD是“通用模塊”定義。瀏覽器模塊開發的規範。
CMD推崇依賴就近、延遲執行。
AMD和CMD區別是:AMD是依賴提前定義;CMD是使用的時候再require。
自身可以
CommonJS和Node.js
CommonJS是服務器端模塊的規範,Node.js採用了這個規範。
CommonJS規範一個單獨的文件就是一個模塊,加載模塊使用require方法,該方法讀取一個文件並執行。最後返回文件內部的exports對象。
CommonJS是同步加載模塊的。所以只有加載完才能繼續後面的操作。
Node.js主要是服務端編程,所以加載的模塊一般存於本地磁盤,加載起來比較快,不用考慮異步加載的方式。
ES6 Module
ES6在語言的標準上實現模塊功能,而且實現的很簡單,
ES6模塊功能主要由兩個命令構成:export和import。export命令用於規定模塊的對外接口,import命令用於輸入其他模塊提供的功能。
ES6的設計思想,是儘量靜態化,使得編譯時就可以確認模塊的依賴關係,以及輸入和輸出的變量。
ES6模塊不是對象,而是通過export命令顯示指定輸出的代碼,輸入也使用靜態命令的形式。
ES6 Module可以在編譯時就完成模塊編譯,效率比CommonJS要高。