談到NodeJS的模塊機(jī)制湿弦,這還得從JavaScript這門語言說起庙楚。JavaScript語言可謂是編程中比較資深的一種語言了莲趣。從上個世紀(jì)(對就是上個世紀(jì))就開始流行了样悟,起初只是作為一些靜態(tài)網(wǎng)頁的簡單控制腳本和特效腳本而存在,而且書寫規(guī)范也沒有標(biāo)準(zhǔn)的官方定義攘滩,<script>標(biāo)簽引入JavaScript代碼帅刊,零零散散的整個網(wǎng)站應(yīng)用到處都是它的蹤跡,這種書寫方式對于一些小型的簡單web應(yīng)用還能夠勝任轰驳,但是隨著技術(shù)的發(fā)展,出現(xiàn)了像jquery,easyui等這樣的封裝庫,大大提升了開發(fā)效率级解,但是畢竟還是一些封裝庫冒黑,對于后期出現(xiàn)的前端工程化應(yīng)用卻只能作為工程支持庫的冰山一角,這時候Javascript模塊機(jī)制就應(yīng)運(yùn)而生了勤哗。首先要講的就是CommonJS規(guī)范抡爹,它是一種標(biāo)準(zhǔn),也是NodeJS常用的一種模塊書寫規(guī)范芒划。
模塊是Node.js 應(yīng)用程序的基本組成部分冬竟,文件和模塊是一一對應(yīng)的。換言之民逼,一個 Node.js 文件就是一個模塊泵殴,這個文件可能是JavaScript 代碼、JSON 或者編譯過的C/C++ 擴(kuò)展拼苍。
CommonJS模塊規(guī)范
CommonJS規(guī)范的提出主要是為了彌補(bǔ)JavaScript沒有標(biāo)準(zhǔn)的缺陷笑诅,以達(dá)到像Java,C#,Python等具備開發(fā)大型應(yīng)用的基礎(chǔ)能力。我們可以像Java那樣一個文件定義一個類(模塊)疮鲫,工程里進(jìn)行相互引用吆你。有了這項能力,我們就可以用NodeJS開發(fā)以下應(yīng)用:
- 命令行工具
- 服務(wù)器端JavaScript應(yīng)用程序
- 桌面圖形應(yīng)用程序(后面會專門去講NodeJS圖形應(yīng)用開發(fā))
CommonJS規(guī)范規(guī)范主要包括:1.模塊引用 2.模塊定義3.模塊標(biāo)識三個部分俊犯。
1.模塊引用
模塊通過require()進(jìn)行引用妇多,示例:
var http=require("http");//引入http模塊
2.模塊定義
上面我們引入的是官方定義的http模塊,當(dāng)然我們也可以引入我們自定義的模塊燕侠,首先我們需要通過exports定義者祖,如下:
新建 lession_exports.js 代碼如下:
/*模塊接口方法定義(導(dǎo)出)*/
exports.add=function(a,b)
{
return a+b;
};
接著新建 lession_require.js 進(jìn)行引入我們剛剛自定義的模塊(模塊中含有一個add方法)
/*模塊機(jī)制*/
var modules_define=require("./lession_exports");//require("./lession_exports.js")這樣引入也可以
var result=modules_define.add(15,16);
console.log("調(diào)用模塊add方法,結(jié)果:"+result);
------------------------
運(yùn)行輸出結(jié)果:
調(diào)用模塊add方法贬循,結(jié)果:31
剛剛我們定義并導(dǎo)出了一個方法咸包,那可能你會問了:我想導(dǎo)出一個類(或?qū)ο?該怎么辦?接下來我們就來講一下怎么導(dǎo)出一個對象的方法杖虾。
新建 lession_modules_exports.js 代碼如下:
/*模塊接口方法定義(導(dǎo)出)*/
//定義一個類
function People(name)
{
this.name=name;
this.sleep=function()
{
console.log(this.name+" 睡覺了");
};
this.speak=function()
{
console.log(this.name+" 說話了");
};
}
module.exports=People;//導(dǎo)出
在我們前面創(chuàng)建的 lession_require.js 文件中添加代碼:
var People=require("./lession_modules_exports.js");//引入定義的類
var people=new People("Codingyu");//實例化一個類對象people
people.sleep();//調(diào)用people實例對象的sleep方法
people.speak();//調(diào)用people實例對象的speak方法
---------------------
執(zhí)行__ node lession_require.js __烂瘫,輸出結(jié)果如下:
Codingyu 睡覺了
Codingyu 說話了
以上是NodeJS模塊機(jī)制CommonJS規(guī)范的基本使用。接下來我們再談一下AMD規(guī)范和CMD規(guī)范以及怎么編寫前后端兼容的模塊奇适。
AMD模塊規(guī)范
之前我們講NodeJS前后端通吃坟比,既可以做前端又可以做后端服務(wù)器,那這樣我們在開發(fā)中就可以前后端有些模塊功能共享代碼嚷往,加快開發(fā)進(jìn)度葛账。但是如果前后端都使用CommonJS規(guī)范,會有一個問題皮仁,CommonJS規(guī)范require加載模塊是同步的籍琳,在服務(wù)器端調(diào)用直接讀取磁盤是瞬間的極快的菲宴,對于加載速度沒有任何影響,但是對于前端來說趋急,由于前端是通過網(wǎng)絡(luò)加載的喝峦,所以會產(chǎn)生require速度問題。AMD模塊規(guī)范對于模塊的引入是異步的呜达,所以比較適合前端的模塊管理谣蠢。接下來我們就來舉個栗子,但是在舉栗子之前我們需要先簡單掌握一下node里的一個很強(qiáng)大的包管理功能----NPM(我在這一章節(jié)會仔細(xì)去講解npm包管理http://www.reibang.com/p/445d0168d691)
在NodeJS中使用AMD模塊規(guī)范和后面要講的CMD模塊規(guī)范,需要使用npm去下載 ‘a(chǎn)mdefine’:
npm install amdefine
amdefine安裝完畢后查近,接下來我們
新建 lession_exports_amd.js 代碼如下:
/*AMD模塊定義導(dǎo)出*/
//首先需要在文件中引入amdefine模塊
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
//定義自己的模塊
define(function()
{
var exports={};
exports.add=function(a,b)
{
return a+b;
};
return exports;//導(dǎo)出對象
});
接著新建 lession_require_amd.js 去測試一下,代碼如下:
/*AMD模塊機(jī)制*/
//首先需要在文件中引入amdefine模塊
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
//定義自己的模塊
//方法原型:define(["dep1","dep2"],function(dep1,dep2){});
define(["./lession_exports_amd"],function(exportAMD)
{
var result=exportAMD.add(15,16);//調(diào)用另一個使用AMD規(guī)范定義的模塊接口方法
console.log("通過AMD規(guī)范調(diào)用接口add方法結(jié)果:"+result);
return {};
});
OK!我們可以運(yùn)行一下lession_require_amd.js眉踱,測試一下AMD模塊規(guī)范的使用,結(jié)果輸出:
通過AMD規(guī)范調(diào)用接口add方法結(jié)果:31
CMD模塊規(guī)范
CMD模塊規(guī)范其實是建立在AMD規(guī)范之上的霜威,是由國內(nèi)的玉伯提出谈喳,與AMD規(guī)范主要區(qū)別在于定義模塊和依賴引入的部分:
define(function(require,exports,module)
{
......
});
require,exports,module通過形參的方式傳遞給模塊,在需要依賴引入時侥祭,隨時調(diào)用require("xxx")叁执,和CommonJS很像。
再舉個栗子矮冬,新建 lession_exports_cmd.js 谈宛,代碼如下:
/*CMD模塊定義導(dǎo)出*/
//首先需要在文件中引入amdefine模塊
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
//定義自己的模塊
define(function(require,exports,module)
{
var my={};
my.add=function(a,b)
{
return a+b;
};
module.exports=my;
});
接著新建 lession_require_cmd.js 去測試一下CMD規(guī)范,代碼如下:
/*CMD模塊機(jī)制*/
//首先需要在文件中引入amdefine模塊
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
//定義自己的模塊
define(function(require,exports,module)
{
var my=require("./lession_exports_cmd");
var result=my.add(15,16);//調(diào)用另一個使用CMD規(guī)范定義的模塊接口方法
console.log("通過CMD規(guī)范調(diào)用接口add方法結(jié)果:"+result);
});
執(zhí)行l(wèi)ession_require_cmd.js,結(jié)果輸出:
通過CMD規(guī)范調(diào)用接口add方法結(jié)果:31
AMD胎署、CMD吆录、CommonJS不同的規(guī)范,前端說:我們使用AMD/CMD,后端說:我們使用CommonJS琼牧,剛剛我們講恢筝,有一些前后端通用的模塊代碼,那我們能做到前后端模塊規(guī)范兼容統(tǒng)一嗎巨坊?答案是肯定的撬槽,舉個栗子
新建 lession_exports_compat.js 代碼如下:
/*兼容模式模塊定義導(dǎo)出*/
//定義自己的模塊
(function(name,definition)
{
var hasDefine=typeof define==="function";//是否有define的定義
var hasExports=typeof module!=="undefined"&&module.exports;//是否有module和exports的定義
if(hasDefine)
{
//AMD或者CMD環(huán)境
define(definition);
}else if(hasExports)
{
//CommonJS規(guī)范(普通node模塊)
module.exports=definition();
}else{
//普通window環(huán)境將模塊直接掛載到window對象上
this[name]=definition();
}
})('my',function () {
//真正定義模塊內(nèi)容的地方
var my={};
my.add=function(a,b)
{
return a+b;
};
return my;
});
接下來再創(chuàng)建測試文件 lession_require_compat.js,代碼如下:
/*兼容模式模塊定義導(dǎo)出*/
//首先需要在文件中引入amdefine模塊----兼容模式測試可以不引入直接使用CommonJS規(guī)范
// if (typeof define !== 'function') {
// var define = require('amdefine')(module);
// }
//定義自己的模塊
(function(name,definition)
{
var hasDefine=typeof define==="function";//是否有define的定義
var hasExports=typeof module!=="undefined"&&module.exports;//是否有module和exports的定義
if(hasDefine)
{
//AMD或者CMD環(huán)境
define(definition);
}else if(hasExports)
{
//CommonJS規(guī)范(普通node模塊)
module.exports=definition();
}else{
//普通window環(huán)境將模塊直接掛載到window對象上
this[name]=definition();
}
})('myrequire',function () {
//正在引入模塊使用的地方
var my=require("./lession_exports_compat");
var result=my.add(15,16);
console.log(result);
return {};
});
很簡單吧趾撵!大體結(jié)構(gòu)就是一個自執(zhí)行函數(shù)侄柔,上面我代碼里注掉的地方,打開它便會產(chǎn)生AMD/CMD環(huán)境占调,注釋它則會默認(rèn)產(chǎn)生CommonJS環(huán)境暂题,這樣定義的模塊我們前后端就可以隨意copy過來使用了(當(dāng)然還有一些前后端不同API環(huán)境的影響)。