人人都会AI编程

内置模块、第三方模块、自定义模块划分

更新时间:2026-07-11

在 Node.js 中,模块化是组织代码的核心手段。一个 Node.js 程序通常由三类模块构成:内置模块(由 Node.js 官方提供)、第三方模块(从 npm 安装的社区模块)和自定义模块(开发者自己编写的模块)。清楚地区分这三者,理解它们的加载方式和使用场景,是编写可维护、可扩展 Node.js 应用的第一步。

内置模块:无需安装,开箱即用

内置模块是 Node.js 运行时自带的模块,它们在安装 Node.js 时就已经存在,无需通过 npm 额外下载。这些模块封装了最常用的系统能力,例如文件操作、网络通信、路径处理、操作系统信息等。使用内置模块时,只需通过 require('模块名') 直接引入即可。

常见的内置模块速览

  • fs:文件系统操作(读取、写入、删除、目录遍历等)
  • http / https:创建 HTTP/HTTPS 服务器与客户端
  • path:路径拼接、解析、规范化(跨平台兼容)
  • os:获取操作系统信息(CPU 架构、内存大小、网络接口等)
  • stream:处理流式数据(大文件读写、数据管道)
  • crypto:加解密、哈希、证书等安全功能
  • events:事件驱动的核心模块,提供 EventEmitter
  • util:工具函数(类型检查、promisify 等)
  • process:全局进程对象,提供环境变量、命令行参数等

内置模块的版本与 Node.js 版本绑定,随着 Node.js 的升级而更新。它们通常非常稳定,API 变更会遵循 Node.js 的 LTS 与版本兼容性政策。在 require 时不需要指定路径,Node.js 会直接从内部缓存中获取对应模块。

// 使用内置模块:读取文件
const fs = require('fs');
const path = require('path');

const filePath = path.join(__dirname, 'data.txt');
fs.readFile(filePath, 'utf-8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

某些内置模块还带有实验性质的 API,使用时需要 Node.js 版本满足要求,并且 API 可能在未来版本中改变,生产环境建议优先选择稳定 API。

第三方模块:npm 生态的庞大工具箱

第三方模块是通过 npm(Node Package Manager)安装的外部包,托管在 npm 仓库中。从 Web 框架(Express、Koa、Fastify)到数据库驱动(mysql2、pg、mongodb),再到工具库(lodash、axios、dayjs),几乎涵盖了所有通用需求。

安装与使用第三方模块

  1. 在项目根目录下通过 npm init 创建 package.json(如果还未创建)。
  2. 使用 npm install 包名 或简写 npm i 包名 安装模块。模块会被下载到 node_modules 目录,并在 package.json 中记录依赖。
  3. 在代码中通过 require('包名') 引入模块。与内置模块类似,第三方模块也不需要路径前缀,Node.js 会自动在 node_modules 中查找。
// 使用第三方模块:搭建一个简易 HTTP 服务
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello from Express');
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

依赖管理与版本控制

  • package.jsondependencies 字段记录生产环境必需的模块。
  • devDependencies 字段记录仅在开发阶段使用的模块(如测试框架、构建工具)。
  • 通过 npm install 会自动安装所有列出的依赖。
  • package-lock.json 锁定了依赖的精确版本,确保团队环境一致。

第三方模块的查找规则

当执行 require('express') 时,Node.js 会按照以下顺序查找:

  1. 检查核心模块(内置模块)中是否有同名模块。
  2. 从当前文件的 node_modules 目录开始,逐级向上查找,直到找到包含该包的 node_modules 目录或到达根目录。
  3. 如果仍找不到,则抛出 MODULE_NOT_FOUND 错误。

这种机制使得项目可以依赖多个不同版本的同一包(例如 node_modules 中可能出现嵌套的依赖),但大型项目中也可能因此造成 node_modules 体积膨胀,如今主流包管理器(如 pnpm、yarn)已经对这个问题进行了优化。

自定义模块:项目内部的代码组织

自定义模块是指开发者自己在项目中编写的 .js 文件或目录,通过 require() 引用,从而实现代码复用和逻辑拆分。例如,将数据校验、工具函数、数据库操作等抽成单独的模块。

创建一个自定义模块

新建一个文件 math.js

function add(a, b) {
  return a + b;
}

function multiply(a, b) {
  return a * b;
}

module.exports = { add, multiply };

在另一个文件中使用:

const math = require('./math');
console.log(math.add(2, 3)); // 5
console.log(math.multiply(2, 3)); // 6

exports 与 module.exports 的区别

  • 每个模块内部,module.exports 是最终暴露的对象。
  • exportsmodule.exports 的一个引用,刚开始它们指向同一个空对象。
  • 如果给 exports 重新赋一个新对象,会切断它与 module.exports 的联系;而 module.exports 重新赋值则可以彻底改变导出内容。

常见的最佳实践是统一使用 module.exports,避免混淆。

require 路径规则

  • 相对路径:以 ./../ 开头,表示相对于当前文件的路径。
  • 绝对路径:以 / 开头,表示从文件系统根目录开始查找(不常用)。
  • 文件扩展名可以省略,Node.js 会按 .js.json.node 的顺序自动补全。
  • 当路径指向一个目录时,Node.js 会查找目录下的 package.json 并读取 main 字段指定的文件;如果没有 package.jsonmain 字段,则尝试加载 index.jsindex.node。这使得目录被视为一个完整的模块。

模块缓存

Node.js 对已加载的模块会进行缓存。当 require 一个模块时,如果该模块已经被加载过,则会直接返回缓存的 module.exports 对象,而不会再次执行模块代码。因此:

  • 同一个模块在多次 require 后得到的是同一个对象实例(单例模式)。
  • 但基于文件路径区分,不同路径下可能有独立副本。
  • 缓存机制提升了性能,避免了重复解析和编译。

这种缓存特性对于共享状态(如数据库连接池)非常有用,但也可能导致一些意想不到的单例问题,后续在模块系统原理章节中会深入讨论。

三类模块的区分汇总

| 类型 | 来源 | 引入方式 | 是否需要安装 | 示例 |
|------------|--------------------|------------------------|--------------|-----------------------------------------|
| 内置模块 | Node.js 自带 | require('模块名') | 否 | fs, http, path, os |
| 第三方模块 | npm 仓库 | require('包名') | 是 | express, axios, lodash |
| 自定义模块 | 项目文件 | require('./文件路径') | 否 | ./utils/logger.js, ./config/db.js |

在实际开发中,一个典型的 Node.js 文件可能同时引入这三类模块:

// 内置模块
const fs = require('fs');
const path = require('path');

// 第三方模块
const express = require('express');
const morgan = require('morgan');

// 自定义模块
const userService = require('./services/userService');
const config = require('./config/app');

掌握这三种模块的划分和用法,就拥有了构建 Node.js 项目的基石。后面章节还将深入模块系统的底层原理、循环引用的处理以及 ES Modules 与 CommonJS 的互操作,帮助你在更复杂的场景中游刃有余。