module.exports和exports,應該用哪個

2023-08-25 06:00:38

在 Node.js 程式設計中,模組是獨立的功能單元,可以在專案間共用和重用。作為開發人員,模組讓我們的生活更輕鬆,因為我們可以使用模組來增強應用程式的功能,而無需親自編寫。它們還允許我們組織和解耦程式碼,從而使應用程式更易於理解、偵錯和維護。

在這篇文章中,我將介紹如何在 Node.js 中使用模組,重點是如何匯出和消費它們。

各種模組格式

由於 JavaScript 最初沒有模組的概念,因此隨著時間的推移,出現了各種相互競爭的格式。下面列出了需要注意的主要格式:

  • Asynchronous Module Definition (AMD)格式用於瀏覽器,使用define函數來定義模組。
  • CommonJS (CJS)格式用於Node.js,使用requiremodule.exports來定義依賴和模組。npm 生態系統就是基於這種格式構建的。
  • ES Module (ESM)格式。從 ES6(ES2015)開始,JavaScript 支援原生模組格式。它使用 export 關鍵字彙出模組的公共 API,使用 import 關鍵字彙入模組。
  • System.register格式用於支援 ES5 中的 ES6 模組。
  • Universal Module Definition (UMD)格式可以用於瀏覽器和Node.js。當一個模組需要被多個不同的模組載入器匯入時,它就會非常有用。

請注意,本文僅涉及 Node.js 的標準 CommonJS格式。

引入模組

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}.`
);

匯出預設值

上面的範例中,我們單獨匯出了函數和值。這對於整個應用程式都可能需要的輔助函數來說非常方便,但當你有一個只匯出一樣東西的模組時,使用 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, '[email protected]');

console.log(jim.getUserStats());

程式碼輸出如下:

Name: Jim
Age: 37
Email: [email protected]

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}.`
);

那麼,module.exportsexports的不同之處是什麼?一個只是另一個的別名嗎?

有點,但不完全是……

為了闡明我的意思,我們更改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屬性。在exports上新增一些東西:

// index.js
exports.foo = 'foo';
console.log(module);

輸出如下:

Module {
  id: '.',
  exports: { foo: 'foo' },
  ...

exports 分配的屬性也會將它們新增到 module.exports。這是因為(至少最初)exports 是對 module.exports 的參照。

應該用哪個

由於 module.exportsexports 都指向同一個物件,因此使用哪個通常並不重要。例如:

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 中使用模組,並幫助你揭開模組語法的神祕面紗。

以上就是本文的全部內容,如果對你有所幫助,歡迎點贊、收藏、轉發~