在編程領域中,模塊是自包含的功能單元,可以跨項目共享和重用。它們使開發人員的生活更加輕松,因為我們可以使用它來增加應用程序的功能,而不必親自編寫這些功能。它還讓我們可以組織和解耦代碼,從而使應用程序更容易理解、調試和維護。
在本文中,我們來探究如何使用 Node.js 中的模塊,主要介紹如何導出和導入。
不同的模塊格式
由於JavaScript 最初沒有模塊的概念,隨着時間的推移出現了各種相互競爭的格式。以下是主流的幾種格式:
- Asynchronous Module Definition (AMD)格式, 用於瀏覽器端,使用
define
函數定義模塊。 - CommonJS (CJS) 格式,用於 Node.js,使用
require
和module.exports
定義依賴和模塊。 - ES Module (ESM) 格式。從 ES6 (ES2015)開始,JavaScript 支持原生模塊格式。它使用
export
關鍵字導出模塊的公開 API,並用import
關鍵字導入。 - System.register 格式被設計用於在ES5 中 支持 ES6 模塊。
- Universal Module Definition (UMD) 格式,在瀏覽器和 Node.js 中都可以使用。當模塊需要被多種模塊加載程序導入時,這個很有用。
請注意,本文只討論 CommonJS 格式,它是Node.js 中的標准格式。如果你想深入了解其他格式,我推薦你閱讀這篇文章,作者是 SitePoint 的 Jurgen Van de Moere。
加載模塊
Node.js 有一系列的內置模塊,我們在代碼中無需安裝即可使用。為此,我們需要使用require
關鍵字加載這個模塊,並把它賦值給一個變量。這樣就可以調用模塊暴露的任何方法了。
例如,要列出目錄的內容,你可以使用文件系統模塊 的 readdir
方法:
const fs = require('fs');
const folderPath = '/home/jim/Desktop/';
fs.readdir(folderPath, (err, files) => {
files.forEach(file => {
console.log(file);
});
});
注意,在 CommonJS 里,模塊是按照出現的順序同步加載和處理的。
創建和導出模塊
現在我們來看看如何創建模塊並導出,以便在程序的其他地方使用。先創建一個 user.js
文件,並添加如下內容。
const getName = () => {
return 'Jim';
};
exports.getName = getName;
然后在同一個目錄中創建一個 index.js
文件,添加以下內容:
const user = require('./user');
console.log(`User: ${user.getName()}`);
用命令node index.js
運行程序,你應該會在控制台看到如下輸出:
User: Jim
這里發生了什么?如果你查看一下user.js
文件,你會注意到我們定義了一個getName
函數,然后使用exports
關鍵字使其可以在其他地方導入。然后是 index.js
文件,我們導入了這個方法並執行它。還要注意的是在require
語句中,模塊名字加了前綴 ./
,因為它是本地文件。另外就是不需要加文件擴展名。
導出多個方法和值
我們可以用同樣的方式導出多個方法和值:
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}.`
);
上面的代碼結果如下:
Jim lives in Munich and was born on 12.01.1982.
注意,我們為導出的dateOfBirth
變量指定的名稱可以是任何我們想要的名稱(本例中為dob
)。它不必與原來的變量名相同。
多種語法形式
還需要提到的是,你可以在中途導出方法和值,並不一定要在文件末尾。
例如:
exports.getName = () => {
return 'Jim';
};
exports.getLocation = () => {
return 'Munich';
};
exports.dob = '12.01.1982';
另外要感謝解構賦值,我們可以根據需要選擇性導入:
const { getName, dob } = require('./user');
console.log(
`${getName()} was born on ${dob}.`
);
如你所料,這會輸出日志:
Jim was born on 12.01.1982.
導出默認值
在上面的例子中,我們分別導出了函數和值。這對於整個應用程序都需要的輔助函數來說是很方便的,但是當你的模塊只導出單個對象時,通常使用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
的區別是什么
你在網上可能會遇到以下語法:
module.exports = {
getName: () => {
return 'Jim';
},
getLocation: () => {
return 'Munich';
},
dob: '12.01.1982',
};
這里我們把要導出的函數和值賦給 module
的 exports
屬性——當然這也是可以的:
const { getName, dob } = require('./user');
console.log(
`${getName()} was born on ${dob}.`
);
輸出日志如下:
Jim was born on 12.01.1982.
那么, module.exports
和 exports
之間的區別到底是什么?后者只是個別名嗎?
嗯,有一點,但不完全是。
為了說明我的意思,讓我們更改index.js
中的代碼,輸出module
的值:
console.log(module);
輸出結果為:
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
有一個exports
屬性。讓我們再加點東西:
// index.js
exports.foo = 'foo';
console.log(module);
這會輸出:
Module {
id: '.',
exports: { foo: 'foo' },
...
給exports
添加屬性,也會添加到 module.exports
。這是因為exports
是module.exports
的一個引用。
我應該用哪個?
既然 module.exports
和 exports
都指向同一個對象,你使用哪一個通常無關緊要。例如:
exports.foo = 'foo';
module.exports.bar = 'bar';
這段代碼將會使模塊的導出對象變成{ foo: 'foo', bar: 'bar' }
。
不過,這里有個需要注意的地方。你賦值給module.exports
的內容將成為模塊導出的值。
舉例如下:
exports.foo = 'foo';
module.exports = () => { console.log('bar'); };
這樣只會導出一個匿名函數。foo
變量將被忽略。
如果你想了解更多,我推薦你閱讀 這篇文章。
總結
模塊已經成為 JavaScript 生態系統不可分割的一部分,它讓我們能用更小的部分組成大型程序。我希望本文為你在 Node.js 中使用模塊做了良好介紹,以及幫助你了解模塊語法。
作者:James Hibbard
來源:SitePoint
翻譯:1024譯站
更多前端技術干貨盡在微信公眾號:1024譯站