1. node.js模塊概述
為了讓node.js的文件可以相互調用汤善,node.js提供了一個簡單的模塊系統(tǒng)。模塊是node.js應用程序基本的組成部分票彪,文件和模塊是一一對應的红淡。換言之,一個node.js文件就是一個模塊降铸,這個文件可能是javascript代碼在旱、json或者編譯過的c/c++擴展。
其中http推掸、fs桶蝎、net等都是node.js提供的核心模塊驻仅,使用c/c++實現,外部用javascript封裝登渣。
2. 創(chuàng)建模塊的兩種方式
創(chuàng)建模塊有兩種方式噪服,
- 通過exports創(chuàng)建
- 通過module.exports創(chuàng)建
2.1 通過exports創(chuàng)建模塊
node.js中,創(chuàng)建一個模塊非常簡單胜茧,我們創(chuàng)建一個main.js文件粘优,它引用了hello模塊,代碼如下呻顽,
var hello = require('./hello')
hello.world()
在上面的代碼中雹顺,require('./hello')引入了當前目錄下的hello.js文件。
./代表當前目錄廊遍,node.js默認后綴為js嬉愧。
node.js提供了exports和require兩個對象,其中exports是模塊公開的接口喉前,require用于從外部獲取一個模塊的接口没酣,即所獲取模塊的exports對象。
接下來我們創(chuàng)建hello.js文件卵迂,如下代碼所示四康,
exports.world = function() {
console.log('hello world')
}
以上示例中,hello.js通過exports對象把world作為模塊的訪問接口狭握,在main.js中通過require('./hello')加載這個模塊闪金,然后就可以直接訪問hello.js中exports對象的成員函數了。
2.2 通過module.exports創(chuàng)建模塊
有時候我們只是想把一個對象封裝到模塊中论颅,如下格式哎垦,
module.exports = function() {
}
以上面的格式,來寫一個模塊恃疯,如下hello.js代碼漏设,
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName
}
this.sayHello = function() {
console.log('hello ' + name)
}
}
module.exports = Hello
這樣就可以直接獲取這個對象了,如下main.js代碼今妄,
// main.js
var Hello = require('./hello')
hello = new Hello()
hello.setName('BYVoid')
hello.sayHello()
模塊接口的唯一變化是使用module.exports = Hello代替了exports.world = function() {}郑口。在外部引用該模塊時,其接口對象就是要輸出的Hello對象本身盾鳞,而不是原先的exports犬性。
2.3 exports和module.exports區(qū)別
為了更好地解釋exports和module.exports之間的關系,先通過一個簡單的js示例來做一個說明腾仅,如下代碼乒裆,
var a = {name: 1}
var b = a
console.log(a)
console.log(b)
b.name = 2
console.log(a)
console.log(b)
b = {name: 3}
console.log(a)
console.log(b)
運行test.js結果為,
{ name: 1 }
{ name: 1 }
{ name: 2 }
{ name: 2 }
{ name: 2 }
{ name: 3 }
簡單解釋一下上面的代碼:a是一個對象推励,b是對a對象的引用鹤耍,此時a和b只想同一塊內存肉迫,所以前兩個輸出一樣;當對b做修改時稿黄,則a和b只想同一塊內存地址的內容發(fā)生了改變喊衫,所以a的值改變也體現了出來;當b被覆蓋時杆怕,b只想了一塊新的內存格侯,而a還是只想原來的內存,所以最后兩個輸出不一樣财著。
明白了上面的例子之后,只需要指點3點就能了解exports和module.exports的區(qū)別了撑碴,
- module.exports初始值為一個空對象{}
- exports是只想module.exports的引用
- require()返回的是module.exports而不是exports
也就是說撑教,module.exports才是真正的接口,exports只不過是它的一個輔助工具醉拓。最攻返回給調用者的是module.exports而不是exports伟姐。
再強調一點,在node.js中亿卤,一個文件對應一個模塊愤兵。為了方便,模塊中會有一個exports對象排吴,它和module.exports指向同一個變量秆乳,所以我們修改exports對象的時候也會修改module.exports對象;當我們通過賦值方式為module.exports賦值時候钻哩,此時module.exports與exports對象指向的變量就不同了屹堰,所以無論exports對象怎么改,都和module.exports對象沒有任何關系了街氢。
加粗扯键!加粗!加粗珊肃!一般來說荣刑,推薦使用module.exports,盡量少使用exports伦乔。
3. require搜索module的方式
在node.js中模塊有兩種類型厉亏,即,
- 核心模塊
- 文件模塊
3.1 搜索核心模塊
核心模塊直接使用名稱獲取烈和,例如經常使用的http模塊叶堆,使用如下代碼獲取,
var http = require('http')
...
http.createServer()
簡要描述一下上面的代碼斥杜,node.js中自帶了一個叫做http的模塊虱颗,在上述代碼中我們請求它并把返回的值賦值給一個本地變量沥匈,這樣本地變量就編程了一個擁有所有http模塊所提供的公共方法的對象。
3.2 搜索文件模塊
在前面創(chuàng)建模塊的demo中忘渔,通過require('./hello')語法高帖,如下代碼,
var Hello = require('./hello')
hello = new Hello()
hello.setName('BYVoid')
...
...
這里畦粮,我們使用./test來獲取自定義文件模塊散址,這種通過相對路徑或絕對路徑是文件模塊的搜索方式。
3.3 搜索模塊的規(guī)則
node.js加載模塊時宣赔,遵循了如下的加載規(guī)則预麸,
- 核心模塊優(yōu)先級最高,直接使用名字加載儒将,再有命名沖突的時候首先加載核心模塊
- 文件模塊只能按照路徑加載 -- 相對路徑或絕對路徑吏祸,并且可以省略默認的.js后綴名
- 查找node_modules目錄,當我們在調用npm install <name>命令的時候钩蚊,會在當前目錄下創(chuàng)建node_module目錄來安裝模塊贡翘,當require遇到一個既不是核心模塊,又不是以路徑形式表示的模塊名稱時砰逻,會試圖在當前目錄下的node_modules目錄中查找是不是有這樣一個模塊鸣驱。如果沒有找到,則會在當前目錄的上一層的node_modules目錄中繼續(xù)查找蝠咆,反復執(zhí)行這一過程踊东,知道遇到根目錄位置。
相對路徑 - 例如:
./hello
表示同級目錄刚操,../hello
表示上層目錄絕對路徑 - 例如:
/Users/user/Desktop/js/hello