原文:module.exports – How to Export in Node.js and JavaScript,作者:Dillion Megida

在编程中,模块是一个程序的组成部分,具有一个或多个功能或值。

这些值也可以在整个程序中共享,并可以以不同的方式使用。

在这篇文章中,我将告诉你如何在 Node.js 中通过导出和导入模块来共享函数和值。

为什么要导出模块

你想导出模块,这样你就可以在你的应用程序的其他部分使用它们。

模块可以有不同的用途。它们可以提供简单的实用程序来修改字符串,可以提供 API 请求的方法,或者甚至可以提供常量和原始值。

当你导出一个模块时,你可以把它导入到你的应用程序的其他部分并使用它。

Node.js 支持 CommonJS 模块ECMAScript 模块

在本文的其余部分,我们将专注于 CommonJS 模块,这是 Node.js 中打包模块的原始方法。

如果你想了解更多关于 ES 模块(以及 CommonJS 模块)的信息,你可以查看这份详细指南

如何在 Node 中导出模块

Node.js 已经导出了内置的模块,例如 fspathhttp。但你也可以创建你自己的模块。

Node.js 将 Node 项目中的每个文件视为一个模块,可以从文件中导出值和功能。

例如,你有一个 utility 文件 utility.js,代码如下:

// utility.js

const replaceStr = (str, char, replacer) => {
  const regex = new RegExp(char, "g")
  const replaced = str.replace(regex, replacer)
  return replaced
}

utility.js 是一个模块,其他文件可以从中导入东西。但是 utility.js 目前并没有输出任何东西。

你可以通过检查每个文件中的全局 module 对象来验证这一点。当你打印这个 utility 文件中的 module 全局对象时,你有:

console.log(module)

// {
//   id: ".",
//   path: "...",
//   exports: {},
//   parent: null,
//   filename: "...",
//   loaded: false,
//   children: [],
//   paths: [
//     ...
//   ],
// }

module 对象有一个 exports 属性,正如你所看到的,它是一个空对象。

因此,任何试图从这个文件中导入任何东西的行为都会产生错误。

utility.js 文件有一个 replaceStr 方法,可以用一些其他字符替换字符串中的字符。我们可以从这个模块导出这个函数,供其他文件使用。

方法如下:

// utility.js

const replaceStr = (str, char, replacer) => {
  const regex = new RegExp(char, "g")
  const replaced = str.replace(regex, replacer)
  return replaced
}

module.exports = { replaceStr }
// or
exports.replaceStr = replaceStr

现在, replaceStr 可以在应用程序的其他部分使用。要使用它,你要像这样导入它:

const { replaceStr } = require('./utility.js')

// then use the function anywhere

module.exports vs exports in Node

你可以使用 module.exports 从一个模块中导出函数和值:

module.exports = { value1, function1 }

或者使用 exports

exports.value1 = value1
exports.function1 = function1

区别是什么?

这些方法是相同的。基本上,exports 是对 module.exports 的引用。为了更好地理解这一点,让我们通过使用两种导出值的方式来填充 exports 对象:

const value1 = 50
exports.value1 = value1

console.log(module)
// {
//   id: ".",
//   path: "...",
//   exports: { value1: 50 },
//   parent: null,
//   filename: "...",
//   loaded: false,
//   children: [],
//   paths: [
//     ...
//   ],
// }

const function1 = function() {
  console.log("I am a function")
}
module.exports = { function1, ...module.exports }

console.log(module)

// {
//   id: ".",
//   path: "...",
//   exports: { function1: [Function: function1] },
//   parent: null,
//   filename: "...",
//   loaded: false,
//   children: [],
//   paths: [
//     ...
//   ],
// }

这里有两件事需要注意:

  • exports 关键字是对 modules 对象中 exports 对象的引用。通过 exports.value1 = value1,它将 value1 属性添加到 module.exports 对象中,正如你在第一个日志中看到的那样。
  • 第二条日志不再包含 value1 的导出。它只有使用 module.exports 导出的函数。为什么会这样呢?

module.exports = ... 是将一个新对象重新分配给 exports 属性的一种方式。这个新对象只包含函数,所以 value1 不再被导出。

那么有什么区别呢?

只用 exports 关键字导出数值是一种快速导出模块中数值的方法。你可以在顶部或底部使用这个关键字,它所做的只是填充 module.exports 对象。但如果你在一个文件中使用 exports,请坚持在整个文件中使用它。

使用 module.exports 是一种明确指定模块导出的方式。在一个文件中最好只存在一次。如果它存在两次,第二次声明就会重新分配 module.exports 属性,而模块只输出第二次声明中的内容。

因此,作为前面代码的解决方案,你可以像这样导出:

// ...
exports.value1 = value1

// ...
exports.function1 = function1

或者像这样:

// ...
module.exports = { value1, function1 }

总结

Node.js 项目中的每个文件都被视为一个模块,可以导出值供其他模块使用。

module.exports 是 Node.js 文件中的一个对象,用于保存该模块导出的值和函数。

在一个文件中声明一个 module.exports 对象,指定从该文件中导出的值。导出后,另一个模块可以通过 require 全局方法导入这些值。