深入理解 Node.js 模块系统

71 阅读2分钟

1. 什么是模块?

在 Node.js 中,模块是功能代码的封装单元,模块化帮助开发者将大项目拆解成多个独立的小模块,每个模块可以提供一组特定的功能。 Node.js 支持以下几种模块:

  • 内置模块:Node.js 自带的模块,如 fshttppath 等。
  • 用户自定义模块:由开发者创建的模块。
  • 第三方模块:通过 npm 安装的模块,如 expresslodash 等。

2. 内置模块

Node.js 自带了许多内置模块,开发者可以直接使用它们,无需额外安装。常见的内置模块包括:

  • http:用于创建 HTTP 服务器和客户端。
  • fs:用于文件系统操作,如读写文件。
  • path:用于处理文件路径。
  • os:提供操作系统相关的信息。
  • events:用于事件驱动编程。

例如,使用 http 模块创建一个简单的服务器:

const http = require('http');

const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello, Node.js!');
});

server.listen(3000, () => {
    console.log('Server is running on http://localhost:3000');
});

3. 用户自定义模块

除了内置模块,Node.js 还允许开发者创建自定义模块。每个模块都是一个单独的文件,可以导出需要共享的函数、对象或变量。

创建一个自定义模块 math.js

// math.js
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;

在另一个文件中引入并使用该模块:

// app.js
const math = require('./math');

console.log(math.add(5, 3));  // 输出: 8
console.log(math.subtract(5, 3));  // 输出: 2

4. require 与模块加载

require 是 Node.js 加载模块的机制。它不仅支持加载内置模块,还支持加载本地文件模块和第三方模块。加载的过程是同步的,Node.js 会缓存已经加载的模块,以提高性能。

  • 加载内置模块时直接使用模块名,如 require('http')
  • 加载本地模块时,路径是必需的,比如 require('./math')
  • 第三方模块需要先通过 npm install 安装。

5. 模块的作用域

每个模块都有自己的作用域。模块内的变量和函数默认是私有的,不能被外部访问。通过 exportsmodule.exports 对外暴露接口,供其他模块使用。

// foo.js
const secret = 'I am secret';
exports.reveal = () => secret;
// bar.js
const foo = require('./foo');
console.log(foo.reveal());  // 输出: I am secret

6. 模块缓存

Node.js 会缓存已经加载的模块。如果你多次 require 同一个模块,Node.js 会返回缓存中的模块,而不是重新加载它。这有助于提高性能,避免重复计算和资源浪费。

7. 模块的导入与导出方式

7.1、导出模块: 使用 module.exports 或 exports 将函数、对象或变量导出。

7.2、导入模块: 使用 require() 导入模块。

  • module.exports:最直接的方式,完全替代 exports,可以导出一个单一的对象或函数。
// math.js
module.exports = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b,
};
// app.js
const math = require('./math');
console.log(math.add(2, 3));  // 输出: 5
  • exports:通过 exports 对象添加多个接口,但需要注意不能直接赋值给 exports,否则会失去原有的引用。
// math.js
exports.add = (a, b) => a + b;