你是不是也经常为了一个小功能就往 package.json 里塞一个 npm 包?其实,随着 Node.js 版本不断迭代,很多原本要借助第三方库才能实现的能力,已经直接进入了运行时内置 API。
近年来,Node.js 开发人员依赖无数的 npm 包 来填补平台的空白。从 HTTP 工具到文件系统助手,庞大的生态一直是 Node.js 最大的优势之一。但随着 Node.js 的不断发展,许多曾经需要第三方包的功能现在已直接集成在运行时中。这种转变减少了依赖项的臃肿,提高了安全性,也让应用更容易维护。
下面就来梳理 15 个最值得关注的 Node.js 新功能,它们可以在不少场景中替代流行的 npm 包。
1. node-fetch → 全局 fetch()
以前: 开发人员安装 node-fetch 来在 Node.js 中使用熟悉的浏览器 fetch() API。
现在: 从 Node.js 18 开始,fetch() 成为全局函数,与浏览器实现相同。
const res = await fetch('https://api.github.com/repos/nodejs/node');
const data = await res.json();
console.log(data.full_name); // "nodejs/node"
引入时间: 在 Node.js v17.5.0 中引入(实验性)。
稳定时间: 在 Node.js v18.0.0 中成为稳定版本,不再标记为实验性。
何时仍应使用 node-fetch: 仅当需要旧版 Node.js 兼容性(低于 18)时。
2. ws(客户端)→ 全局 WebSocket
以前: ws 包是 WebSocket 客户端和服务端的首选方案。
现在: Node.js 内置了一个 全局 WebSocket 类,用于客户端连接:
const ws = new WebSocket('wss://echo.websocket.org');
ws.onopen = () => ws.send('Hello!');
ws.onmessage = (event) => console.log('Received:', event.data);
引入时间: 在 Node.js v21.0.0 中添加了实验性的全局 WebSocket 客户端支持。
稳定时间: 截至目前仍处于实验阶段,尚未声明为稳定版本。
何时仍应使用 ws: 对于服务端 WebSocket 实现,ws 或基于它的库仍然是标准选择。
3. 测试框架 → node:test
以前: 写测试通常需要引入 mocha、jest 或 tap 这样的第三方测试库。
现在: Node.js 内置了 node:test,一个开箱即用的测试运行器:
import test from 'node:test';
import assert from 'node:assert';
test('addition works', () => {
assert.strictEqual(2 + 2, 4);
});
引入时间: 在 Node.js v18.0.0 中引入(实验性),详见官方文档。
稳定时间: 自 Node.js v20.0.0 起被认为是稳定的。
何时仍应使用第三方框架: 如果你需要快照测试、模拟(mocking)或丰富的插件生态等功能,第三方框架仍然更合适。对于模块级测试来说,node:test 已经足够;但对于包含全栈能力的较大型应用开发,Jest 这类框架依然有自己的优势。
4. sqlite3 / better-sqlite3 → node:sqlite [实验性]
以前: 开发者依赖原生绑定,如 sqlite3 或更快的 better-sqlite3。这些库往往需要编译,并且经常在升级时遇到问题。
现在: Node.js 正在引入一个实验性的 node:sqlite 模块:
import { open } from 'node:sqlite';
const db = await open(':memory:');
await db.exec('CREATE TABLE users (id INTEGER, name TEXT)');
当前状态: 仍为实验性功能。
何时仍应使用社区包: 如果你需要高级性能调优,或者当前实验 API 尚未提供的功能,社区包依然是更好的选择。
5. chalk / kleur → util.styleText()
以前: chalk 和 kleur 这样的库主导了控制台输出样式。
现在: Node.js 提供了 util.styleText():
import { styleText } from 'node:util';
console.log(styleText('red', 'Error!'));
console.log(styleText(['bold', 'green'], 'Success!'));
引入时间: 在 Node.js v20.12.0 中引入。
稳定时间: 自 Node.js v22.17.0 起稳定。
何时仍应使用 chalk: 如果你需要丰富的主题、链式语法或向后兼容性,chalk 依然有用。
6. ansi-colors / strip-ansi → util.stripVTControlCharacters()
以前: 开发者使用 strip-ansi 等包来清理日志中的 ANSI 转义码。
现在: Node.js 内置了 util.stripVTControlCharacters():
import { stripVTControlCharacters } from 'node:util';
const text = '\u001B[4mUnderlined\u001B[0m';
console.log(stripVTControlCharacters(text)); // "Underlined"
优点: 原生可靠地处理 ANSI 代码。
何时仍应使用第三方包: 很少——大多数场景现在都可以通过原生方式处理。
7. glob → fs.glob()
以前: glob 包对于文件模式匹配至关重要。
现在: Node.js 22+ 引入了 fs.glob():
import fs from 'node:fs/promises';
const files = await fs.glob('**/*.js');
console.log(files);
引入时间: 在 Node.js v22.0.0 中加入,作为 fs API 扩展的一部分;v22.0 的发布说明中包含了新的 fs glob 功能。
稳定时间: 在 Node.js 22.17.0 LTS 中稳定,属于 v22 发行线。
何时仍应使用 glob: 如果你需要与旧版 Node.js 兼容。
8. rimraf → fs.rm({ recursive: true })
以前: 递归删除目录需要引入 rimraf。
现在: Node.js 原生支持直接递归删除:
import fs from 'node:fs/promises';
await fs.rm('dist', { recursive: true, force: true });
引入时间: fs.rm() 的 recursive 选项自 Node.js v12.10.0 起可用于传统回调用法,后续版本在 Promise API 中也支持该选项。官方文档显示,fs.rm(path[, options]) 自 Node.js 14+ 起就包含 recursive 支持,force 选项同样受支持。
稳定时间: 在所有活跃的 LTS 版本(v18、v20、v22)中已经稳定,不再是实验性功能。
9. mkdirp → fs.mkdir({ recursive: true })
以前: 开发者使用 mkdirp 递归创建目录。
现在: Node.js 原生支持:
await fs.mkdir('logs/app', { recursive: true });
引入时间: fs.mkdir() 的 recursive 选项在 Node.js v10.12.0 中加入。
稳定时间: 自引入以来一直稳定,它已成为核心 API 的一部分并被广泛使用。
10. uuid(v4)→ crypto.randomUUID()
以前: 生成 UUID 意味着要添加 uuid 包。
现在: Node.js 内置了 crypto.randomUUID():
import { randomUUID } from 'node:crypto';
console.log(randomUUID());
引入时间: 在 Node.js v14.17.0 中引入。
稳定时间: 自引入以来保持稳定,属于 crypto 核心模块。
11. base64-js / atob polyfills → Buffer、atob、btoa
以前: Base64 编码/解码通常需要 polyfill。
现在: Node.js 内置了 atob 和 btoa 全局变量,以及 Buffer:
const encoded = btoa('hello');
console.log(encoded); // "aGVsbG8="
console.log(atob(encoded)); // "hello"
引入时间: atob 和 btoa 全局变量在 Node.js v20.0.0 或之后引入。
稳定时间: 它们是当前 Node.js LTS 发行版中稳定 API 的一部分。
12. url-pattern → URLPattern [实验性]
以前: 开发者依赖 url-pattern 进行路由匹配。
现在: Node.js 内置了全局 URLPattern API:
const pattern = new URLPattern({ pathname: '/users/:id' });
const match = pattern.exec('/users/42');
console.log(match.pathname.groups.id); // "42"
引入时间: URLPattern 在 Node.js v20.0.0 中作为实验性功能引入。
稳定时间: 目前仍标记为实验性,尚未稳定。
13. dotenv(基础用法)→ --env-file 标志 [实验性]
以前: 加载 .env 文件需要引入 dotenv。
现在: Node.js 可以直接加载环境文件:
node --env-file=.env app.js
引入时间: --env-file 标志在 Node.js v20.10.0 中引入(实验性)。
稳定时间: 尚未稳定,仍为实验性。
何时仍应使用 dotenv: 如果你需要变量扩展或多环境文件等高级功能,dotenv 仍然值得保留。
14. event-target-shim → EventTarget
以前: Node.js 有一套自己的 EventEmitter,开发者使用 event-target-shim 来获得 Web 标准的 EventTarget 能力。
现在: EventTarget 可以全局使用:
const target = new EventTarget();
target.addEventListener('ping', () => console.log('pong'));
target.dispatchEvent(new Event('ping'));
引入时间: 在 Node.js v15.0.0 中引入。
稳定时间: 在 Node.js v15.4.0 中成为稳定版本,当时被标记为“不再实验性”。
15. tsc(基本转换)→ Node.js 实验性 TypeScript 支持
以前: 运行 .ts 文件需要完整的 TypeScript 工具链,要么是 tsc,要么是 ts-node。
现在: Node.js 内置了 实验性 TypeScript 支持:
node --experimental-strip-types app.ts
引入时间: 这个功能非常新。“strip types”实验性标志在 Node.js v21.0.0 或该主要版本附近引入。官方文档显示在 21.x 发行线中已有实验性支持,不过资料中没有完整记录具体的小版本号。
稳定时间: 尚未稳定,仍为实验性。
何时仍应使用 tsc: 对于完整的类型检查、声明文件生成和生产级构建,TypeScript 官方工具链仍然是必选项。
总结
Node.js 的发展表明了一个明确的趋势:曾经需要外部依赖的功能,如今正逐步成为核心能力。这种转变帮助开发者:
- 减少依赖项开销
- 最小化供应链和安全风险
- 编写在浏览器和服务器之间更具可移植性的代码
本文翻译整理自 NodeSource 博客:Node.js Features That Are Replacing npm Packages。