近年来, Node.js 开发者对 npm 包的依赖程度一直很高——从 HTTP 工具库到文件系统辅助,庞大的生态系统向来是 Node.js 最大的优势。然而随着运行时自身不断演进,许多以前必须借助第三方包实现的特性,现在已经直接集成到平台中了。
这种变化意味着更少的依赖臃肿、更强的安全性以及更易维护的项目。假如你也想全面监控 Node.js 应用中的第三方包安全风险,N|Solid 这类工具同样值得关注。下面我们就来梳理一下那些足以取代流行 npm 包的 Node.js 新功能。
1. node-fetch → 全局 fetch()
以前:为了在 Node.js 里使用熟悉的浏览器 fetch() API,大家都会安装 node-fetch。
现在:从 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 起归为稳定。
何时仍需第三方框架:当你需要快照测试、模拟(mock)或者丰富的插件生态时。对于一般模块测试,node:test 已经足够,不过那些大型全栈测试框架在应用开发中仍有用武之地。
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 之类的包来清理日志里的转义码。
现在: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 扩展加入。
稳定时间:在 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 中也进行了支持。
稳定时间:在所有活跃的 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
以前:编码/解码常需借助 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"
引入时间:Node.js v20.0.0 作为实验性功能加入。
稳定时间:目前仍标记为实验性(即尚未稳定)。
13. dotenv(基础)→ --env-file 标志 [实验性]
以前:加载 .env 文件基本靠 dotenv。
现在:Node.js 可以直接从环境文件中加载:
node --env-file=.env app.js
引入时间:Node.js v20.10.0 引入(实验性)。
稳定时间:尚未稳定,仍为实验性。
何时仍需 dotenv:如果你需要变量扩展、多环境文件等高级能力,dotenv 依然是可靠选择。
14. event-target-shim → EventTarget
以前:Node.js 自己有一套独有的 EventEmitter,想用 Web 标准的 EventTarget 还得靠 event-target-shim。
现在: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
引入时间:--experimental-strip-types 标志在 Node.js v21.0.0 或相近版本引入。
稳定时间:尚未稳定——仍为实验性。
何时仍需 tsc:想要完整的类型检查、声明文件生成以及生产级构建时,TypeScript 编译器依然是必需品。
总结
Node.js 的演进昭示了一个明确的趋势:曾经必须依赖外部包的功能,正越来越多地化身为核心特性。这一转变帮助开发者:
- 降低依赖项开销。
- 最大限度地减少供应链与安全风险。
- 让代码在浏览器和服务器之间具备更强的可移植性。
有意识地拥抱这些原生能力,不仅能精简你的项目依赖树,还能让应用更好地适应未来 Node.js 的演进节奏。在下一期项目中,不妨试试用内置方案替换一两个习以为常的第三方包,说不定你会有“早该如此”的感慨。
云栈社区持续关注 Node.js 及全栈技术的生态演进,欢迎同行一起探讨与交流。
-已完结-