node_modules 太胖?用 Node.js 原生功能给依赖做一次大扫除

node_modules 太胖?用 Node.js 原生功能给依赖做一次大扫除

写 Node.js 项目的时候,package.json 里的依赖列表是不是越来越长?

装个 node-fetch 发请求,装个 uuid 生成 ID,装个 dotenv 读环境变量,装个 chalk 输出彩色日志......每个包都不大,但加起来就是一堆。

问题是:

  • 依赖多了,供应链风险就大了(还记得 left-pad 事件吗?)
  • node_modules 越来越臃肿
  • 版本冲突和兼容性问题时不时冒出来

好消息是,Node.js 这几年一直在把常用功能收编进核心模块。很多以前必须装包才能用的东西,现在原生就有了。

这篇文章整理了 15 个可以被 Node.js 原生功能替代的 npm 包,按使用频率排序,看看哪些依赖可以从你的项目里删掉。

1. fetch() 替代 node-fetch

这个应该是用得最多的了。

以前 Node.js 没有 fetch,想发 HTTP 请求要么用 http 模块自己封装,要么装 node-fetch。现在不用了:

javascript 复制代码
// Node.js 18+ 原生支持
const res = await fetch('https://api.github.com/repos/nodejs/node');
const data = await res.json();
console.log(data.stargazers_count);
  • v17.5.0 实验性引入
  • v18.0.0 稳定

API 和浏览器的 fetch 一样,代码可以前后端通用。

2. crypto.randomUUID() 替代 uuid

生成 UUID 是个很常见的需求,以前都用 uuid 包:

javascript 复制代码
// 以前
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4();

// 现在(Node.js 14.17.0+)
import { randomUUID } from 'node:crypto';
const id = randomUUID();
// 输出类似:'1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'

少装一个包,还不用担心 uuid 的版本兼容问题。

3. --env-file 替代 dotenv

.env 文件是几乎每个项目都要做的事,以前必须装 dotenv

javascript 复制代码
// 以前
import 'dotenv/config';
console.log(process.env.API_KEY);

现在可以用命令行参数:

bash 复制代码
# Node.js 20.10.0+
node --env-file=.env app.js

不用写任何代码,环境变量直接加载到 process.env

要注意的是,--env-file 功能比 dotenv 简单,不支持变量展开(${VAR})和多行值。如果你的 .env 文件比较复杂,可能还是得用 dotenv

4. fs.rm({ recursive: true }) 替代 rimraf

删除目录及其所有内容,以前用 rimraf

javascript 复制代码
// 以前
import rimraf from 'rimraf';
rimraf.sync('dist');

// 现在(Node.js 12.10.0+)
import { rm } from 'node:fs/promises';
await rm('dist', { recursive: true, force: true });

force: true 表示目录不存在时不报错,和 rm -rf 行为一致。

5. fs.mkdir({ recursive: true }) 替代 mkdirp

创建嵌套目录:

javascript 复制代码
// 以前
import mkdirp from 'mkdirp';
mkdirp.sync('path/to/nested/dir');

// 现在(Node.js 10.12.0+)
import { mkdir } from 'node:fs/promises';
await mkdir('path/to/nested/dir', { recursive: true });

这个功能加得比较早,很多项目可能已经在用了。

6. node:test 替代测试框架

Node.js 现在有内置的测试模块:

javascript 复制代码
// test.js
import { test } from 'node:test';
import assert from 'node:assert';

test('加法测试', () => {
  assert.strictEqual(1 + 2, 3);
});

test('异步测试', async () => {
  const result = await Promise.resolve(42);
  assert.strictEqual(result, 42);
});

运行:

bash 复制代码
node --test
  • v18.0.0 实验性引入
  • v20.0.0 稳定

对于模块级别的单元测试够用了。但如果是大型应用,需要 mock、覆盖率报告、并行执行等功能,Jest 或 Vitest 还是更合适。

7. util.styleText() 替代 chalk

终端彩色输出:

javascript 复制代码
// 以前
import chalk from 'chalk';
console.log(chalk.red('Error!'));
console.log(chalk.green.bold('Success!'));

// 现在(Node.js 20.12.0+,v22.17.0 稳定)
import { styleText } from 'node:util';
console.log(styleText('red', 'Error!'));
console.log(styleText(['green', 'bold'], 'Success!'));

支持的样式包括:redgreenyellowbluebolditalicunderline 等。

8. util.stripVTControlCharacters() 替代 strip-ansi

去除 ANSI 转义字符(彩色输出的控制码):

javascript 复制代码
import { stripVTControlCharacters } from 'node:util';

const colored = '\x1b[31mError\x1b[0m';
const plain = stripVTControlCharacters(colored);
// plain === 'Error'

在写日志到文件或者做字符串比较时很有用。

9. fs.glob() 替代 glob

文件匹配:

javascript 复制代码
// 以前
import { glob } from 'glob';
const files = await glob('**/*.js');

// 现在(Node.js 22.0.0+)
import { glob } from 'node:fs/promises';
const files = await glob('**/*.js');

API 基本一致,迁移成本很低。

10. atob / btoa 替代 Base64 polyfill

Base64 编解码:

javascript 复制代码
// Node.js 20+ 全局可用
const encoded = btoa('hello world');
// 'aGVsbG8gd29ybGQ='

const decoded = atob(encoded);
// 'hello world'

和浏览器 API 一致,写同构代码更方便了。

11. WebSocket 替代 ws(客户端场景)

javascript 复制代码
// Node.js 21.0.0+(实验性)
const ws = new WebSocket('wss://echo.websocket.org');

ws.addEventListener('open', () => {
  ws.send('Hello');
});

ws.addEventListener('message', (event) => {
  console.log('Received:', event.data);
});

目前还是实验性的,而且只适合客户端场景。写 WebSocket 服务端,ws 包还是标准选择。

12. node:sqlite 替代 sqlite3 / better-sqlite3

javascript 复制代码
// 实验性功能
import { open } from 'node:sqlite';

const db = await open(':memory:');
await db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
await db.run('INSERT INTO users (name) VALUES (?)', 'Alice');
const row = await db.get('SELECT * FROM users WHERE id = ?', 1);

最大的好处是不用编译原生模块了。sqlite3better-sqlite3 都需要 node-gyp 编译,在某些环境下会遇到各种问题。

目前还是实验性的,生产环境建议观望。

13. EventTarget 替代 event-target-shim

javascript 复制代码
// Node.js 15.0.0+ 全局可用
const target = new EventTarget();

target.addEventListener('message', (event) => {
  console.log(event.detail);
});

target.dispatchEvent(new CustomEvent('message', { detail: 'hello' }));

和浏览器 API 一致。

14. URLPattern 替代 url-pattern

路由匹配:

javascript 复制代码
// Node.js 20.0.0+(实验性)
const pattern = new URLPattern({ pathname: '/users/:id' });

const result = pattern.exec('https://example.com/users/123');
console.log(result.pathname.groups.id); // '123'

对于简单的路由匹配场景够用,但目前还是实验性的。

15. --experimental-strip-types 运行 TypeScript

bash 复制代码
# Node.js 21.0.0+(实验性)
node --experimental-strip-types app.ts

这个功能只是去掉类型注解,不做类型检查,也不支持一些 TypeScript 特有的语法(如 enumnamespace)。

适合快速运行 .ts 文件做原型或测试,但不能替代完整的 TypeScript 工具链。

哪些可以直接用,哪些再等等?

功能 状态 建议
fetch() 稳定 直接用
crypto.randomUUID() 稳定 直接用
--env-file 实验性 简单场景可用
fs.rm/mkdir 稳定 直接用
node:test 稳定 模块测试可用
util.styleText() 稳定 直接用
fs.glob() 稳定 直接用
atob/btoa 稳定 直接用
WebSocket 实验性 客户端可用
node:sqlite 实验性 观望
URLPattern 实验性 观望
TypeScript 支持 实验性 原型/测试可用

迁移建议

  1. 先查版本 :确认你的 Node.js 版本支持这些功能。node -v 看一下。

  2. 渐进式迁移:不用一次全换,哪个包用得多、问题多,先换哪个。

  3. 实验性功能谨慎使用:标记为实验性的功能,API 可能会变,生产环境慎用。

  4. 保留复杂场景的包 :如果你需要 dotenv 的变量展开、chalk 的链式调用、Jest 的完整测试能力,继续用包也没问题。原生功能是多了个选择,不是必须替换。

依赖少了,项目就干净了。供应链安全、启动速度、维护成本,都能受益。

参考


如果你觉得这篇文章有帮助,欢迎关注我的 GitHub,下面是我的一些开源项目:

Claude Code Skills (按需加载,意图自动识别,不浪费 token,介绍文章):

全栈项目(适合学习现代技术栈):

  • prompt-vault - Prompt 管理器,用的都是最新的技术栈,适合用来学习了解最新的前端全栈开发范式:Next.js 15 + React 19 + tRPC 11 + Supabase 全栈示例,clone 下来配个免费 Supabase 就能跑
  • chat_edit - 双模式 AI 应用(聊天+富文本编辑),Vue 3.5 + TypeScript + Vite 5 + Quill 2.0 + IndexedDB
相关推荐
开心就好20252 小时前
苹果iOS设备免越狱群控系统完整使用指南与应用场景解析
后端
_Kayo_2 小时前
TypeScript 学习笔记2
前端·javascript·typescript
海纳百川本尊760642 小时前
Flutter框架核心原理深度解析
前端
Shaneyxs2 小时前
从 0 到 1 实现CloudBase云开发 + 低代码全栈开发活动管理小程序(12)
前端
ss2732 小时前
SpringBoot+vue养老院运营管理系统
vue.js·spring boot·后端
渔_2 小时前
uni-app 图片预览 + 长按保存,超实用!
前端
八哥程序员2 小时前
从DOM结构到布局流:display: content的深度解析与实战应用
前端·css
Shaneyxs2 小时前
从 0 到 1 实现CloudBase云开发 + 低代码全栈开发活动管理小程序(07)
前端