前端模塊規(guī)范有三種:CommonJs,AMD和CMD巢钓。
CommonJs用在服務(wù)器端喘批,AMD和CMD用在瀏覽器環(huán)境
AMD 是 RequireJS 在推廣過程中對(duì)模塊定義的規(guī)范化產(chǎn)出裁替。
CMD 是 SeaJS 在推廣過程中對(duì)模塊定義的規(guī)范化產(chǎn)出阀溶。
AMD:提前執(zhí)行(異步加載:依賴先執(zhí)行)+延遲執(zhí)行
CMD:延遲執(zhí)行(運(yùn)行到需加載抽莱,根據(jù)順序執(zhí)行)
模塊
- 函數(shù)寫法
function f1(){
//...
}
function f2(){
//...
}
模塊就是實(shí)現(xiàn)特定功能的文件葵陵,把幾個(gè)函數(shù)放在一個(gè)文件里就構(gòu)成了一個(gè)模塊。需要的時(shí)候加載這個(gè)文件蘸泻,調(diào)用其中的函數(shù)即可琉苇。
但這樣做會(huì)污染全局變量,無法保證不與其他模塊發(fā)生變量名沖突悦施,而且模塊成員之間沒什么關(guān)系并扇。
- 對(duì)象寫法
var module = {
star : 0,
f1 : function (){
//...
},
f2 : function (){
//...
}
};
module.f1();
module.star = 1;
模塊寫成一個(gè)對(duì)象,模塊成員都封裝在對(duì)象里抡诞,通過調(diào)用對(duì)象屬性穷蛹,訪問使用模塊成員土陪。但同時(shí)也暴露了模塊成員,外部可以修改模塊內(nèi)部狀態(tài)肴熏。
- 立即執(zhí)行函數(shù)
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
外部無法訪問內(nèi)部私有變量
CommonJs
CommonJS是服務(wù)器端模塊的規(guī)范鬼雀,由Node推廣使用。由于服務(wù)端編程的復(fù)雜性蛙吏,如果沒有模塊很難與操作系統(tǒng)及其他應(yīng)用程序互動(dòng)源哩。使用方法如下:
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
根據(jù)CommonJS規(guī)范:
一個(gè)單獨(dú)的文件就是一個(gè)模塊。每一個(gè)模塊都是一個(gè)單獨(dú)的作用域鸦做,也就是說励烦,在該模塊內(nèi)部定義的變量,無法被其他模塊讀取泼诱,除非定義為
global
對(duì)象的屬性坛掠。輸出模塊變量的最好方法是使用
module.exports
對(duì)象。加載模塊使用
require
方法治筒,該方法讀取一個(gè)文件并執(zhí)行却音,返回文件內(nèi)部的module.exports
對(duì)象
仔細(xì)看上面的代碼狈究,您會(huì)注意到 require
是同步的挤巡。模塊系統(tǒng)需要同步讀取模塊文件內(nèi)容赫蛇,并編譯執(zhí)行以得到模塊接口。
然而句灌, 這在瀏覽器端問題多多。
瀏覽器端欠拾,加載 JavaScript 最佳胰锌、最容易的方式是在 document
中插入<script>
標(biāo)簽。但腳本標(biāo)簽天生異步藐窄,傳統(tǒng) CommonJS 模塊在瀏覽器環(huán)境中無法正常加載资昧。
解決思路之一是,開發(fā)一個(gè)服務(wù)器端組件荆忍,對(duì)模塊代碼作靜態(tài)分析格带,將模塊與它的依賴列表一起返回給瀏覽器端。 這很好使刹枉,但需要服務(wù)器安裝額外的組件叽唱,并因此要調(diào)整一系列底層架構(gòu)。
另一種解決思路是微宝,用一套標(biāo)準(zhǔn)模板來封裝模塊定義:
define(function(require, exports, module) {
// The module code goes here
});
這套模板代碼為模塊加載器提供了機(jī)會(huì)棺亭,使其能在模塊代碼執(zhí)行之前,對(duì)模塊代碼進(jìn)行靜態(tài)分析蟋软,并動(dòng)態(tài)生成依賴列表镶摘。
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規(guī)范進(jìn)行頁面開發(fā)需要用到對(duì)應(yīng)的庫函數(shù)碌冶,也就是大名鼎鼎RequireJS
,實(shí)際上AMD 是 RequireJS
在推廣過程中對(duì)模塊定義的規(guī)范化的產(chǎn)出
它采用異步方式加載模塊贡未,模塊的加載不影響它后面語句的運(yùn)行种樱。所有依賴這個(gè)模塊的語句,都定義在一個(gè)回調(diào)函數(shù)中俊卤,等到加載完成之后嫩挤,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。
RequireJS
主要解決兩個(gè)問題
- 多個(gè)js文件可能有依賴關(guān)系消恍,被依賴的文件需要早于依賴它的文件加載到瀏覽器
- js加載的時(shí)候?yàn)g覽器會(huì)停止頁面渲染岂昭,加載文件越多,頁面失去響應(yīng)時(shí)間越長
RequireJs也采用require()語句加載模塊狠怨,但是不同于CommonJS约啊,它要求兩個(gè)參數(shù):
第一個(gè)參數(shù)[module],是一個(gè)數(shù)組佣赖,里面的成員就是要加載的模塊恰矩;第二個(gè)參數(shù)callback,則是加載成功之后的回調(diào)函數(shù)憎蛤。math.add()與math模塊加載不是同步的外傅,瀏覽器不會(huì)發(fā)生假死。
require([module], callback);
require([increment'], function (increment) {
increment.add(1);
});
define()函數(shù)
RequireJS
定義了一個(gè)函數(shù) define
俩檬,它是全局變量萎胰,用來定義模塊:
define(id?, dependencies?, factory);
參數(shù)說明:
id:指定義中模塊的名字,可選棚辽;如果沒有提供該參數(shù)技竟,模塊的名字應(yīng)該默認(rèn)為模塊加載器請(qǐng)求的指定腳本的名字。如果提供了該參數(shù)屈藐,模塊名必須是“頂級(jí)”的和絕對(duì)的(不允許相對(duì)名字)榔组。
依賴dependencies:是一個(gè)當(dāng)前模塊依賴的,已被模塊定義的模塊標(biāo)識(shí)的數(shù)組字面量联逻。
依賴參數(shù)是可選的瓷患,如果忽略此參數(shù),它應(yīng)該默認(rèn)為["require", "exports", "module"]遣妥。然而擅编,如果工廠方法的長度屬性小于3,加載器會(huì)選擇以函數(shù)的長度屬性指定的參數(shù)個(gè)數(shù)調(diào)用工廠方法。工廠方法factory爱态,模塊初始化要執(zhí)行的函數(shù)或?qū)ο筇诽啊H绻麨楹瘮?shù),它應(yīng)該只被執(zhí)行一次锦担。如果是對(duì)象俭识,此對(duì)象應(yīng)該為模塊的輸出值。
來舉個(gè)??看看:
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
(參數(shù)一,參數(shù)二)磁椒;參數(shù)一是數(shù)組堤瘤,傳入我們需要引用的模塊名,第二個(gè)參數(shù)是個(gè)回調(diào)函數(shù)浆熔,回調(diào)函數(shù)傳入一個(gè)變量本辐,代替剛才所引入的模塊。
main.js
//別名配置
requirejs.config({
paths: {
jquery: 'jquery.min' //可以省略.js
}
});
//引入模塊医增,用變量$表示jquery模塊
requirejs(['jquery'], function ($) {
$('body').css('background-color','red');
});
引入模塊也可以只寫require()
慎皱。requirejs
通過define()
定義模塊,定義的參數(shù)上同叶骨。在此模塊內(nèi)的方法和變量外部是無法訪問的茫多,只有通過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規(guī)范是國內(nèi)發(fā)展出來的天揖,就像AMD有個(gè)requireJS
,CMD有個(gè)瀏覽器的實(shí)現(xiàn)SeaJS
缔恳,SeaJS
要解決的問題和requireJS
一樣,只不過在模塊定義方式和模塊加載(可以說運(yùn)行洁闰、解析)時(shí)機(jī)上有所不同歉甚。
在 CMD 規(guī)范中,一個(gè)模塊就是一個(gè)文件扑眉。代碼的書寫格式如下:
define(function(require, exports, module) {
// 模塊代碼
});
require
是可以把其他模塊導(dǎo)入進(jìn)來的一個(gè)參數(shù);而exports
是可以把模塊內(nèi)的一些屬性和方法導(dǎo)出的;module
是一個(gè)對(duì)象纸泄,上面存儲(chǔ)了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法。
AMD是依賴關(guān)系前置,在定義模塊的時(shí)候就要聲明其依賴的模塊;
CMD是按需加載依賴就近,只有在用到某個(gè)模塊的時(shí)候再去require:
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
// 此處略去 100 行
var b = require('./b') // 依賴可以就近書寫
b.doSomething()
// ...
})
// AMD 默認(rèn)推薦的是
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》主要是個(gè)人對(duì)以下文章的總結(jié)腰素,感謝這些老司機(jī)們的分享聘裁。
前端模塊化
詳解JavaScript模塊化開發(fā)
Javascript模塊化編程
從 CommonJS 到 Sea.js