在學(xué)node.js, 實(shí)際上就是基于common.js開(kāi)發(fā)的亚隅,所以了解了一下模塊化開(kāi)發(fā)昧绣。
JS的模塊化初衷和所有語(yǔ)言的模塊化都是一致的珊泳,就是規(guī)范化開(kāi)發(fā)弥雹。
1.最早期的時(shí)候就是原生的js從上到下代碼編寫執(zhí)行缺猛,
function fun1(){ //... }
function fun2(){ //... }
所有的函數(shù)和變量都直接放入相應(yīng)的js文件中缨叫。導(dǎo)致的問(wèn)題就是全局對(duì)象Global被污染椭符,函數(shù)命名沖突。同時(shí)在js文件之間有所依賴時(shí)耻姥,還要保證文件引入的順序销钝,文件之間的依賴關(guān)系不好管理。
2.為了解決上面的問(wèn)題琐簇,采用了使用對(duì)象封裝的模式蒸健。
var module1 = {
property1 : 1,
property2 : 2,
fun1 : function(){},
fun2: function(){}
}
obj.fun1();
好處是減少了對(duì)全局對(duì)象的污染,而新的問(wèn)題是對(duì)象類型的不安全婉商,在外部可以對(duì)其內(nèi)部進(jìn)行修改似忧。
3.而采用匿名閉包就可以達(dá)到隱藏其內(nèi)部細(xì)節(jié)的功能。
var module1 =( function () {
var property1 = 1;
var property2 = 2;
function fun1(){};
function fun2(){};
return { fun1: fun1, fun2: fun2};
}) ();
module1.fun1();
module1.property1 ; // undefined
4.而考慮到依賴丈秩,一種比較典型的方法是JQuery封裝風(fēng)格盯捌。
(function(window){
//代碼
window.jQuery = window.$ = jQuery;
//通過(guò)給window添加屬性而暴漏到全局
})(window);
通過(guò)匿名函數(shù)包裝代碼,所依賴的外部變量傳給這個(gè)函數(shù)癣籽,在函數(shù)內(nèi)部可以使用這些依賴挽唉,然后在函數(shù)的最后把模塊自身暴漏給window。
如果需要添加擴(kuò)展筷狼,則可以作為jQuery的插件瓶籽,把它掛載到$上。
這種風(fēng)格雖然靈活了些埂材,但并未解決根本問(wèn)題:所需依賴還是得外部提前提供塑顺、還是增加了全局變量。
5.而最后一個(gè)問(wèn)題俏险,文件的加載严拒。 過(guò)多的<script>標(biāo)簽引入文件導(dǎo)致請(qǐng)求過(guò)多,加載順序的固定竖独,平行加載裤唠,導(dǎo)致依賴之間的模糊。所以之后出現(xiàn)了LAB.js這種script loader 還有 module loader YUI3等莹痢。實(shí)際上因?yàn)闉g覽器端任務(wù)量較小的緣故种蘸,各種復(fù)雜的JS邏輯也可以執(zhí)行下去。但是進(jìn)入到了服務(wù)器端以后竞膳,更迫切的需要模塊化的存在航瞭。
1.CommonJS規(guī)范
CommonJS是服務(wù)器端模塊的規(guī)范,Node.js采用了這個(gè)規(guī)范坦辟。Node.JS首先采用了js模塊化的概念刊侯。
根據(jù)CommonJS規(guī)范,一個(gè)單獨(dú)的文件就是一個(gè)模塊锉走。每一個(gè)模塊都是一個(gè)單獨(dú)的作用域滨彻,也就是說(shuō)藕届,在該模塊內(nèi)部定義的變量,無(wú)法被其他模塊讀取疮绷,除非定義為global對(duì)象的屬性翰舌。
輸出模塊變量的最好方法是使用module.exports對(duì)象。
var i = 1;
var max = 30;
module.exports = function () {
for (i -= 1; i++ < max; ) {
console.log(i);
}
max *= 1.1;
};
上面代碼通過(guò)module.exports對(duì)象冬骚,定義了一個(gè)函數(shù)椅贱,該函數(shù)就是模塊外部與內(nèi)部通信的橋梁。
加載模塊使用require方法只冻,該方法讀取一個(gè)文件并執(zhí)行庇麦,最后返回文件內(nèi)部的module.exports對(duì)象。
2.AMD
AMD 即Asynchronous Module Definition喜德,中文名是異步模塊定義的意思山橄。它是一個(gè)在瀏覽器端模塊化開(kāi)發(fā)的規(guī)范。
模塊將被異步加載舍悯,模塊加載不影響后面語(yǔ)句的運(yùn)行航棱。所有依賴某些模塊的語(yǔ)句均放置在回調(diào)函數(shù)中。
由于不是JavaScript原生支持萌衬,使用AMD規(guī)范進(jìn)行頁(yè)面開(kāi)發(fā)需要用到對(duì)應(yīng)的庫(kù)函數(shù)饮醇,也就是大名鼎鼎RequireJS,實(shí)際上AMD 是 RequireJS 在推廣過(guò)程中對(duì)模塊定義的規(guī)范化的產(chǎn)出
requireJS主要解決兩個(gè)問(wèn)題
1.多個(gè)js文件可能有依賴關(guān)系秕豫,被依賴的文件需要早于依賴它的文件加載到瀏覽器
2.js加載的時(shí)候?yàn)g覽器會(huì)停止頁(yè)面渲染朴艰,加載文件越多,頁(yè)面失去響應(yīng)時(shí)間越長(zhǎng)
看一個(gè)使用requireJS的例子
// 定義模塊 myModule.js
define(['dependency'], function(){
var name = 'Byron';
function printName(){
console.log(name);
}
return {
printName: printName
};
});
// 加載模塊
require(['myModule'], function (my){
my.printName();
});
requireJS定義了一個(gè)函數(shù) define混移,它是全局變量祠墅,用來(lái)定義模塊
define(id?, dependencies?, factory);
1.id:可選參數(shù),用來(lái)定義模塊的標(biāo)識(shí)歌径,如果沒(méi)有提供該參數(shù)毁嗦,腳本文件名(去掉拓展名)
2.dependencies:是一個(gè)當(dāng)前模塊依賴的模塊名稱數(shù)組
3.factory:工廠方法,模塊初始化要執(zhí)行的函數(shù)或?qū)ο蠡仡酢H绻麨楹瘮?shù)金矛,它應(yīng)該只被執(zhí)行一次。如果是對(duì)象勺届,此對(duì)象應(yīng)該為模塊的輸出值
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
exports.verb = function() {
return beta.verb();
//Or:
return require("beta").verb();
}});
在頁(yè)面上使用require函數(shù)加載模塊
`require([dependencies], function(){});`
require()函數(shù)接受兩個(gè)參數(shù)
1.第一個(gè)參數(shù)是一個(gè)數(shù)組,表示所依賴的模塊
2.第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù)娶耍,當(dāng)前面指定的模塊都加載成功后免姿,它將被調(diào)用。加載的模塊會(huì)以參數(shù)形式傳入該函數(shù)榕酒,從而在回調(diào)函數(shù)內(nèi)部就可以使用這些模塊
require()函數(shù)在加載依賴的函數(shù)的時(shí)候是異步加載的胚膊,這樣瀏覽器不會(huì)失去響應(yīng)故俐,它指定的回調(diào)函數(shù),只有前面的模塊都加載成功后紊婉,才會(huì)運(yùn)行药版,解決了依賴性的問(wèn)題。
3.CMD
CMD 即Common Module Definition通用模塊定義喻犁,CMD規(guī)范是國(guó)內(nèi)發(fā)展出來(lái)的槽片,就像AMD有個(gè)requireJS,CMD有個(gè)瀏覽器的實(shí)現(xiàn)SeaJS肢础,SeaJS要解決的問(wèn)題和requireJS一樣还栓,只不過(guò)在模塊定義方式和模塊加載(可以說(shuō)運(yùn)行、解析)時(shí)機(jī)上有所不同传轰。實(shí)際上本身Seajs就是通過(guò)學(xué)習(xí)并通過(guò)其自己的習(xí)慣改良成的剩盒。
在 CMD 規(guī)范中,一個(gè)模塊就是一個(gè)文件慨蛙。代碼的書寫格式如下:
`define(factory);`
factory 為函數(shù)時(shí)辽聊,表示是模塊的構(gòu)造方法。執(zhí)行該構(gòu)造方法期贫,可以得到模塊向外提供的接口跟匆。factory 方法在執(zhí)行時(shí),默認(rèn)會(huì)傳入三個(gè)參數(shù):require唯灵、exports 和 module:
define(function(require, exports, module) {
// 模塊代碼
});
4.AMD與CMD的區(qū)別
所有的資料都會(huì)告訴你區(qū)別是CMD 推崇依賴就近贾铝,AMD 推崇依賴前置。實(shí)際上依賴就近的含義就是隨用隨require埠帕,而依賴前置的意思就是這一段define需要什么全部在一開(kāi)始的地方require完畢垢揩。
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
//...
var b = require('./b') // 依賴可以就近書寫
b.doSomething()
// ...
})
// AMD 默認(rèn)推薦的是
define(['./a', './b'], function(a, b) { // 依賴必須一開(kāi)始就寫好
a.doSomething()
// ...
b.doSomething()
//...
})
但兩者都是異步操作的。
5.ES6模塊標(biāo)準(zhǔn)
實(shí)際上模塊化已經(jīng)開(kāi)始納入ES標(biāo)準(zhǔn)了敛瓷, 相應(yīng)的ES Module部分才是建議仔細(xì)學(xué)習(xí)的地方叁巨。以后應(yīng)該會(huì)向此靠攏的。
參考:
http://www.cnblogs.com/dolphinX/p/4381855.html
https://segmentfault.com/a/1190000000733959#articleHeader1
http://www.cnblogs.com/lvdabao/p/js-modules-develop.html