Node.js 模塊系統入門


在編程領域中,模塊是自包含的功能單元,可以跨項目共享和重用。它們使開發人員的生活更加輕松,因為我們可以使用它來增加應用程序的功能,而不必親自編寫這些功能。它還讓我們可以組織和解耦代碼,從而使應用程序更容易理解、調試和維護。

在本文中,我們來探究如何使用 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。這是因為exportsmodule.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譯站
微信公眾號:1024譯站


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM