Node.js 2025 工程实践:从依赖外置到运行时内建的迁移清单
很多团队的 Node.js 服务还能跑,但工程基线还停在几年前:require、axios、nodemon、第三方测试框架和一堆小工具包拼在一起,功能没问题,维护成本却越来越高。
我读完这篇文章后最认同的判断是,2025 年的 Node.js 已经不像“一个需要不断补依赖的运行时”,更像一个默认能力越来越完整的后端平台。对工程团队来说,重点不是把所有新特性都用上,而是按优先级把那些已经足够成熟的内建能力收进基线。
如果你维护的是已有服务,我不建议一上来做全量重构。更稳的顺序通常是:
- 模块系统统一到 ESM
- HTTP 调用优先切到原生
fetch - 新增测试优先用
node:test - CPU 热点再评估
worker_threads - 最后补基础观测和诊断能力
这个顺序的好处在于,前两步几乎不要求你改变业务建模,只是把运行时和工具层先收拢。这样迁移收益会来得更快,副作用也更可控。
原文里最有价值的主线,是“把已经内建的能力从依赖里迁回运行时”。
最典型的三件事是:
- CommonJS 迁到 ESM
axios/node-fetch迁到原生fetchnodemon/dotenv的常见用法迁到--watch和--env-file
ESM 不是风格偏好,而是新的默认基线。它和浏览器、边缘运行时以及现代工具链的方向是一致的。再配合 node: 前缀和 top-level await,初始化代码会更清楚,内建模块与第三方包的边界也更清楚。
原生 fetch 的价值也不只是少装一个库。更关键的是它把超时和取消语义重新拉回了标准接口。像 AbortSignal.timeout() 这样的能力一旦统一起来,超时控制、错误映射和重试策略都会更容易收口。
async function requestJson(url) { const response = await fetch(url, { signal: AbortSignal.timeout(5000), })
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`)
return response.json()}这类替换看起来小,但它会持续降低依赖噪音。
node:test 不一定能立刻替代所有现有测试框架,但它已经足够支撑大量单元测试和基础集成测试。
对新项目或者新增测试来说,我会优先把它当默认选择。原因很简单:
- 运行方式直接,维护面更小
- watch 模式内建
- 与运行时版本一致,少一层工具链分歧
如果你强依赖 Jest 的快照体系、复杂 mock 生态或前端同构测试能力,那就没必要强行一步到位。更现实的做法是“新测试先往内建能力走,旧体系按收益逐步迁”。
原文专门把 worker_threads 拿出来讲,这是对的,但也最容易被误用。
很多团队一看到“并行”就想把任务丢进 Worker。实际上它更适合 CPU 密集工作,比如图像处理、复杂压缩、加密或者大批量计算。I/O 密集任务迁过去,收益通常很有限,反而会增加序列化和调试成本。
所以更稳的判断标准不是“能不能并行”,而是“主线程是不是已经被 CPU 热点卡住了”。
import { Worker } from 'node:worker_threads'import { fileURLToPath } from 'node:url'
export function runHeavyTask(payload) { return new Promise((resolve, reject) => { const worker = new Worker( fileURLToPath(new URL('./worker.js', import.meta.url)), { workerData: payload }, )
worker.once('message', resolve) worker.once('error', reject) worker.once('exit', (code) => { if (code !== 0) reject(new Error(`Worker exit code: ${code}`)) }) })}先找热点,再上 Worker,这个顺序不能反。
不少团队一谈可观测性就先上全套平台,但 Node.js 现在已经能先把基础层做好。
原文提到的两个内建能力很实用:
node:perf_hooks用来测关键路径耗时node:diagnostics_channel用来发结构化诊断事件
这类能力不一定能替代完整的 APM,但很适合做第一层观测。先把慢在哪里、什么时候慢、哪类操作最不稳定搞清楚,再决定外部平台要怎么接,投入会更准。
如果要把这篇文章压成一份可执行清单,我会建议这样落地:
- 把 Node 版本基线统一到 20+
- 新模块默认 ESM,内建模块统一改成
node:前缀 - 对外 HTTP 调用优先迁到原生
fetch,并统一超时与取消语义 - 新增测试优先使用
node:test - 开发脚本优先评估
--watch和--env-file - 只对已确认的 CPU 热点引入
worker_threads - 在核心链路上先补
perf_hooks和diagnostics_channel
这张清单的关键不是“做得多快”,而是“先把收益最高、侵入性最低的基线收进来”。
Node.js 在 2025 年最值得关注的变化,不是某个单点新特性,而是默认工程基线整体上移了。
以前你要靠不少第三方库才能搭出来的开发体验,现在有一大块已经进了运行时。对团队来说,这意味着可以更系统地减少依赖、降低工具链分裂,并让运行时行为更可预测。
所以真正实用的策略不是“全面追新”,而是先把那些已经成熟、已经稳定、已经能替代旧依赖的能力收回来。只要 ESM、原生 fetch 和基础测试链路这几步走稳,后面的并发和观测优化就会顺畅很多。