在
Node.js
編程中模狭,module
是可共享和重復(fù)使用的自包含功能單元缩焦。它們使我們作為開(kāi)發(fā)人員的生活更輕松嫂粟,因?yàn)槲覀兛梢允褂盟鼈儊?lái)增強(qiáng)我們的應(yīng)用程序娇未,而無(wú)需自己編寫(xiě)功能。它們還允許我們組織和解耦我們的代碼星虹,從而使應(yīng)用程序更易于理解零抬、調(diào)試和維護(hù)。
不同的Node.js
模塊格式
由于JavaScript最初沒(méi)有模塊的概念宽涌,隨著時(shí)間的推移出現(xiàn)了各種競(jìng)爭(zhēng)的格式平夜。以下是主要的幾種格式:
- 異步模塊定義(
AMD
)格式在瀏覽器中使用,并使用define函數(shù)來(lái)定義模塊护糖。 -
CommonJS
(CJS
)格式在Node.js
中使用褥芒,并使用require
和module.exports
來(lái)定義依賴關(guān)系和模塊。npm
生態(tài)系統(tǒng)是建立在這種格式之上的嫡良。 -
ES
模塊(ESM
)格式锰扶。從ES6(ES2015)
開(kāi)始,JavaScript
支持原生模塊格式寝受。它使用export
關(guān)鍵字來(lái)導(dǎo)出模塊的公共API坷牛,使用import
關(guān)鍵字來(lái)導(dǎo)入它。
System.registe
r格式設(shè)計(jì)用于支持ES5中的ES6模塊很澄。 - 通用模塊定義(
UMD
)格式可以在瀏覽器和Node.js
中使用京闰。當(dāng)模塊需要被多個(gè)不同的模塊加載器導(dǎo)入時(shí),這是非常有用的甩苛。
導(dǎo)入模塊
Node.js自帶一組內(nèi)置模塊蹂楣,我們可以在代碼中使用它們而無(wú)需安裝。為了做到這一點(diǎn)讯蒲,我們需要使用require
關(guān)鍵字來(lái)導(dǎo)入模塊痊土,并將結(jié)果賦值給一個(gè)變量。然后可以使用該變量來(lái)調(diào)用模塊公開(kāi)的任何方法墨林。
例如赁酝,要列出目錄的內(nèi)容,可以使用文件系統(tǒng)模塊及其readdir方法:
const fs = require('fs');
const folderPath = '/home/jim/Desktop/';
fs.readdir(folderPath, (err, files) => {
files.forEach(file => {
console.log(file);
});
});
請(qǐng)注意旭等,在CommonJS
中酌呆,模塊是同步加載的,并按照它們出現(xiàn)的順序進(jìn)行處理搔耕。
創(chuàng)建并導(dǎo)出模塊
現(xiàn)在讓我們看看如何創(chuàng)建自己的模塊并將其導(dǎo)出以供在程序的其他地方使用隙袁。首先創(chuàng)建一個(gè)名為user.js
的文件,并添加以下內(nèi)容:
const getName = () => {
return 'Jim';
};
exports.getName = getName;
現(xiàn)在在同一文件夾中創(chuàng)建一個(gè)名為index.js的文件,并添加以下內(nèi)容:
const user = require('./user');
console.log(`User: ${user.getName()}`);
使用node index.js
運(yùn)行程序藤乙,你應(yīng)該在終端看到以下輸出:
User: Jim
那么這里發(fā)生了什么呢猜揪?如果你看一下user.js
文件惭墓,你會(huì)注意到我們定義了一個(gè)getName
函數(shù)坛梁,然后使用exports
關(guān)鍵字使其可以在其他地方導(dǎo)入。然后在index.js
文件中腊凶,我們導(dǎo)入了這個(gè)函數(shù)并執(zhí)行它划咐。還要注意,在require
語(yǔ)句中钧萍,模塊名以./
為前綴褐缠,因?yàn)樗且粋€(gè)本地文件。還要注意风瘦,不需要添加文件擴(kuò)展名队魏。
導(dǎo)出多個(gè)方法和值
我們可以以相同的方式導(dǎo)出多個(gè)方法和值:
const getName = () => {
return 'Jim';
};
const getLocation = () => {
return 'Munich';
};
const dateOfBirth = '12.01.1982';
exports.getName = getName;
exports.getLocation = getLocation;
exports.dob = dateOfBirth;
然后在index.js中:
const user = require('./user');
console.log(
`${user.getName()} lives in ${user.getLocation()} and was born on ${user.dob}.`
);
以上代碼產(chǎn)生了如下輸出:
Jim lives in Munich and was born on 12.01.1982.
請(qǐng)注意,我們給導(dǎo)出的dateOfBirth
變量起的名稱(chēng)可以是任意的(在這種情況下是dob)万搔。它不必與原始變量名相同胡桨。
語(yǔ)法的變化
我還應(yīng)該提一下,可以在文件中逐步導(dǎo)出方法和值瞬雹,而不僅僅是在文件末尾昧谊。
例如:
exports.getName = () => {
return 'Jim';
};
exports.getLocation = () => {
return 'Munich';
};
exports.dob = '12.01.1982';
由于解構(gòu)賦值的存在,我們可以挑選我們想要導(dǎo)入的內(nèi)容:
const { getName, dob } = require('./user');
console.log(
`${getName()} was born on ${dob}.`
);
正如你所期望的那樣酗捌,這會(huì)輸出:
Jim was born on 12.01.1982.
導(dǎo)出默認(rèn)值
在上面的例子中呢诬,我們單獨(dú)導(dǎo)出函數(shù)和值。這對(duì)于可能在整個(gè)應(yīng)用程序中需要的輔助函數(shù)來(lái)說(shuō)非常方便胖缤,但是當(dāng)您有一個(gè)僅導(dǎo)出一個(gè)東西的模塊時(shí)尚镰,更常見(jiàn)的是使用module.exports
:
class User {
constructor(name, age, email) {
this.name = name;
this.age = age;
this.email = email;
}
getUserStats() {
return `
Name: ${this.name}
Age: ${this.age}
Email: ${this.email}
`;
}
}
module.exports = User;
然后在index.js中:
const User = require('./user');
const jim = new User('Jim', 37, 'jim@example.com');
console.log(jim.getUserStats());
以上代碼輸出:
Name: Jim
Age: 37
Email: jim@example.com
module.exports和exports之間有什么區(qū)別?
在網(wǎng)上您可能會(huì)遇到以下語(yǔ)法:
module.exports = {
getName: () => {
return 'Jim';
},
getLocation: () => {
return 'Munich';
},
dob: '12.01.1982',
};
在這里哪廓,我們將要導(dǎo)出的函數(shù)和值分配給了module
上的一個(gè)exports
屬性 - 當(dāng)然狗唉,這也完全可行:
const { getName, dob } = require('./user');
console.log(
`${getName()} was born on ${dob}.`
);
這將輸出:
Jim was born on 12.01.1982.
那么module.exports
和exports
之間有什么區(qū)別呢?一個(gè)只是另一個(gè)的便捷別名嗎撩独?
嗯敞曹,有點(diǎn),但并非完全如此…
為了說(shuō)明我的意思综膀,讓我們將index.js
中的代碼更改為打印module
的值:
console.log(module);
這會(huì)產(chǎn)生:
Module {
id: '.',
exports: {},
parent: null,
filename: '/home/jim/Desktop/index.js',
loaded: false,
children: [],
paths:
[ '/home/jim/Desktop/node_modules',
'/home/jim/node_modules',
'/home/node_modules',
'/node_modules' ] }
您可以看到澳迫,module
有一個(gè)exports
屬性。讓我們向其中添加一些內(nèi)容:
// index.js
exports.foo = 'foo';
console.log(module);
這會(huì)輸出:
javascript
Copy code
Module {
id: '.',
exports: { foo: 'foo' },
...
向exports
分配屬性也會(huì)將它們添加到module.exports
中剧劝。這是因?yàn)椋ㄖ辽僭陂_(kāi)始時(shí))exports
是對(duì)module.exports
的引用橄登。
應(yīng)該使用哪一個(gè)呢?
由于module.exports
和exports
都指向同一個(gè)對(duì)象,通常使用哪一個(gè)都無(wú)所謂拢锹。例如:
exports.foo = 'foo';
module.exports.bar = 'bar';
這段代碼會(huì)導(dǎo)出模塊的對(duì)象為{ foo: 'foo', bar: 'bar' }
谣妻。
然而,需要注意一點(diǎn)卒稳。無(wú)論您將module.exports
分配給什么蹋半,最終導(dǎo)出的都是您的模塊。
所以充坑,看下面的例子:
exports.foo = 'foo';
module.exports = () => { console.log('bar'); };
這只會(huì)導(dǎo)出一個(gè)匿名函數(shù)减江。foo變量會(huì)被忽略。
其他module.exports
和exports
的常見(jiàn)問(wèn)題
-
module.exports
和exports
在Node.js
中有什么區(qū)別捻爷?
module.export
s和exports
都用于從模塊中導(dǎo)出值辈灼。exports
本質(zhì)上是對(duì)module.exports
的引用,所以您可以任選其一使用也榄。然而巡莹,通常建議使用module.exports
以避免潛在問(wèn)題。
- 我可以在同一個(gè)模塊文件中同時(shí)使用
module.exports
和exports
嗎甜紫?
是的降宅,您可以在同一個(gè)模塊中同時(shí)使用兩者。但在這樣做時(shí)要小心棵介,最好保持一致性钉鸯,堅(jiān)持使用一種慣例。
- 在使用
module.exports
和exports
時(shí)有哪些常見(jiàn)問(wèn)題邮辽?
一個(gè)常見(jiàn)的是直接重新賦值
exports
唠雕,這可能導(dǎo)致意想不到的結(jié)果。最好使用module.exports
以獲得更好的一致性吨述,并避免潛在的問(wèn)題岩睁。
-
module.exports
和exports
之間是否有性能差異?
兩者之間沒(méi)有明顯的性能差異揣云。選擇它們更多地取決于編碼風(fēng)格和約定捕儒。
- 我可以在瀏覽器中使用
module.exports
和exports
嗎,還是它們只適用于Node.js
邓夕?
module.exports
和exports
是特定于Node.js
的刘莹,在瀏覽器的JavaScript
中不可用。在瀏覽器中焚刚,通常使用其他機(jī)制点弯,如ES6
模塊進(jìn)行導(dǎo)出和導(dǎo)入。
總結(jié)
module
已經(jīng)成為JavaScript
生態(tài)系統(tǒng)的一個(gè)重要組成部分矿咕,使我們能夠?qū)⒋笮统绦蚪M合成較小的部分抢肛。