如果你一直在使用 Node.js,可能会有一种很明显的感觉:
Node.js 已经越来越不像一个“只能跑 JavaScript 的后端运行时”了。
Node.js 18 时代,我们开始习惯直接使用 fetch()、node:test;
到了 Node.js 20,Node.js 开始拥有 Permission Model;
Node.js 22 进一步完善 ESM、WebSocket、node --run、文件系统 glob;
Node.js 24 开始把 URLPattern、Float16Array、RegExp.escape()、显式资源管理等现代能力带进运行时;
而 Node.js 26 最值得关注的事情之一,是 Temporal 正式默认开启。
更有意思的是,这些变化并不只是“JavaScript 语法变多了”。
Node.js 本身正在逐渐吸收浏览器标准、Web API、现代模块系统、测试工具、权限控制、开发工具甚至数据库能力。
本文不追求罗列所有 ChangeLog,而是从实际开发的角度,看看 Node.js 18 → 26 到底有哪些值得关注的变化。
先看版本路线
Node.js 使用偶数版本作为主要 LTS 版本线。
截至 2026 年 10 月,目前版本状态大致如下:
| Node.js | 发布时间 | 状态 | 代表性变化 |
|---|---|---|---|
| Node 18 | 2022-04 | EOL | fetch、node:test |
| Node 20 | 2023-04 | EOL | Permission Model、稳定 Test Runner |
| Node 22 | 2024-04 | LTS | WebSocket、node --watch、node --run、require(ESM) |
| Node 24 | 2025-05 | LTS | URLPattern、Float16Array、RegExp.escape、Permission Model 改进 |
| Node 26 | 2026-05 | Current | Temporal、V8 14.6、Undici 8 |
Node.js 官方目前建议生产环境使用 Active LTS 或 Maintenance LTS 版本。
所以如果你现在新建一个项目:
Node 18 ❌ 不建议
Node 20 ❌ 已 EOL
Node 22 ✅
Node 24 ⭐ 当前比较推荐
Node 26 🧪 适合尝鲜和验证新特性
如果是企业项目,实际选择还需要考虑 NestJS、TypeORM、原生模块以及各种三方依赖的兼容性。
Node.js 18:Node.js 开始拥有真正的 Web API
Node.js 18 是一个非常重要的版本。
因为从这个版本开始,Node.js 和浏览器之间的 API 差异开始明显缩小。
1. fetch 正式进入 Node.js
以前 Node.js 写 HTTP 请求,经常需要:
npm install axios
或者:
npm install node-fetch
然后:
const axios = require('axios');
const response = await axios.get('https://example.com');
Node.js 18 开始提供全局 fetch:
const response = await fetch('https://example.com');
const data = await response.json();
console.log(data);
而且这些 Web API 也逐渐成为 Node.js 的标准能力:
fetch
Request
Response
Headers
FormData
Blob
Node.js 18 的 fetch 基于 Undici 实现。
这件事情的重要性其实比看起来大。
因为以前:
浏览器
↓
fetch
Node.js
↓
axios / node-fetch / request
逐渐变成:
浏览器
↓
fetch
Node.js
↓
fetch
这让前后端共享代码变得更加容易。
2. Web Streams 开始进入 Node.js
Node.js 本身一直有:
Readable
Writable
Duplex
Transform
但随着 Web API 的进入,Node.js 开始支持:
ReadableStream
WritableStream
TransformStream
这对于处理:
- HTTP Streaming
- SSE
- AI 流式输出
- 文件流
- Web API
越来越重要。
尤其是现在做 AI 应用:
浏览器
↓
SSE / Stream
↓
Node.js
↓
LLM
Web Streams 的存在让浏览器和 Node.js 的流处理模型越来越接近。
3. node:test:Node.js 开始自己提供测试框架
Node.js 18 还带来了一个很有意思的东西:
import test from 'node:test';
import assert from 'node:assert';
test('addition', () => {
assert.strictEqual(1 + 1, 2);
});
执行:
node --test
这意味着 Node.js 本身开始提供原生测试能力。
以前我们通常会想到:
Jest
Mocha
Vitest
AVA
现在 Node.js 自己就有:
node:test
Node.js 20 又进一步把 Test Runner 标记为稳定。
当然,这并不意味着 Jest、Vitest 就没有意义。
对于大型项目:
Vitest / Jest
↓
完整测试生态
node:test
↓
Node.js 原生测试能力
两者依然可以长期共存。
Node.js 20:开始从“运行时”走向“平台”
Node.js 20 是另一个非常重要的版本。
它最大的特点之一不是某一个 API,而是:
Node.js 开始尝试解决“运行一个 JavaScript 程序到底应该拥有多少权限”这个问题。
4. Permission Model
Node.js 20 引入了实验性的 Permission Model。
例如:
node \
--experimental-permission \
--allow-fs-read=/app \
--allow-fs-write=/tmp \
app.js
这样程序访问文件系统的能力就可以受到限制。
代码:
import fs from 'node:fs';
fs.readFileSync('/etc/passwd');
可能因为没有权限而失败。
Node.js 还提供:
process.permission.has('fs.read');
用于判断当前进程是否拥有对应权限。
Node.js 20 首次引入了这个机制,Node.js 24 又进一步将启动参数从:
--experimental-permission
改进为:
--permission
说明这项能力正在逐渐成熟。
对于普通 Web API 来说,这个功能可能暂时不是刚需。
但是如果你运行:
插件系统
脚本执行平台
AI Agent
Serverless
任务调度器
第三方代码
Permission Model 就非常值得关注。
例如一个 AI Agent:
AI Agent
↓
Node.js
├── 可以访问 /tmp
├── 可以访问项目目录
├── ❌ 不能访问 /etc
├── ❌ 不能启动任意进程
└── ❌ 不能访问其他敏感目录
这比单纯依赖 Docker 隔离多了一层运行时控制。
Node.js 20:Test Runner 终于成熟
Node.js 18 的:
node:test
到了 Node.js 20 已经稳定。
例如:
import { test, describe } from 'node:test';
import assert from 'node:assert';
describe('UserService', () => {
test('should create user', () => {
const user = {
name: 'Tom'
};
assert.strictEqual(user.name, 'Tom');
});
});
然后:
node --test
甚至可以:
node --test --watch
Node.js 20 的 Test Runner 已经支持测试组织、mock、watch mode,以及并行运行测试文件等能力。
对于一些小型 Node.js 项目来说:
Node.js
+
node:test
+
node:assert
已经可以完成相当一部分测试工作。
Node.js 22:现代 Node.js 的分水岭
如果说:
Node 18 = Web API 开始进入 Node.js
Node 20 = Node.js 开始完善平台能力
Node 22 = Node.js 开始真正现代化
我认为这个评价并不过分。
Node.js 22 带来了几个非常值得开发者关注的变化。
5. WebSocket 进入 Node.js
以前 Node.js 写 WebSocket:
npm install ws
然后:
import WebSocket from 'ws';
const ws = new WebSocket('wss://example.com');
ws.on('message', message => {
console.log(message);
});
Node.js 22 开始默认提供 WebSocket Client:
const socket = new WebSocket('wss://example.com');
socket.addEventListener('open', () => {
console.log('connected');
});
socket.addEventListener('message', event => {
console.log(event.data);
});
Node.js 22 的 WebSocket client 默认启用。
于是 Node.js 的 Web API 越来越完整:
fetch
WebSocket
Request
Response
Headers
FormData
Blob
ReadableStream
URL
URLPattern
Node.js 和浏览器之间的边界越来越模糊。
6. node --watch
以前 Node.js 开发经常依赖:
nodemon
例如:
nodemon src/main.js
Node.js 22 将 --watch 标记为稳定:
node --watch src/main.js
文件变化后自动重新启动。
对于简单项目:
Node.js
+
node --watch
已经可以替代:
nodemon
的一部分使用场景。
这也是一个非常典型的趋势:
Node.js 正在不断把以前需要 npm 安装的开发工具,逐渐变成自己的内置能力。
7. node --run
这个功能可能没有 fetch 那么出名,但实际非常方便。
假设:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint ."
}
}
以前:
npm run build
Node.js 22 开始可以:
node --run build
Node.js 会直接读取:
package.json
中的 scripts。
Node.js 22 官方将:
node --run
作为新的命令行能力加入。
它的意义并不是“以后不用 npm 了”。
而是 Node.js CLI 本身开始理解现代 JavaScript 项目的常见工作流。
8. require() 开始可以加载部分 ESM
这是 Node.js 22 非常值得关注的一件事情。
过去 Node.js 的世界经常是:
CommonJS
require()
和:
ESM
import
两个世界互相折磨。
例如:
const xxx = require('xxx');
遇到:
Error [ERR_REQUIRE_ESM]
相信很多维护老项目的人都见过。
Node.js 22 开始支持从 CommonJS 中加载满足条件的同步 ESM graph。
例如:
const module = require('./module.mjs');
不过有一个非常重要的限制:
被
require()加载的 ESM 不能包含顶层await。
当前 Node.js 文档显示,这项能力从 Node.js 22 开始加入,并在后续版本逐渐稳定。
所以 Node.js 现在正在逐渐走向:
CommonJS ───────────┐
├── Node.js
ESM ────────────────┘
而不是过去:
CommonJS
↕
各种 ERR_REQUIRE_ESM
↕
ESM
9. fs.glob():终于不用每个项目装 glob 库了
Node.js 22 开始提供:
import { glob } from 'node:fs/promises';
const files = await glob('src/**/*.ts');
for await (const file of files) {
console.log(file);
}
以前通常需要:
npm install glob
然后:
import { glob } from 'glob';
现在 Node.js 自己开始提供 glob 能力。
这种变化看起来很小。
但是从 Node.js 的长期发展来看非常明显:
以前:
Node.js
+
npm 生态
+
几十个基础工具库
现在:
Node.js
├── fetch
├── WebSocket
├── test
├── watch
├── glob
├── run
└── 更多 Web API
Node.js 的“标准库”正在变得越来越现代。
Node.js 24:开始进入真正的现代 JavaScript Runtime
Node.js 24 于 2025 年 5 月发布。
V8 升级到 13.6,同时带来了:
Float16Array
RegExp.escape
Error.isError
显式资源管理
WebAssembly Memory64
并且 Node.js 24 自带 npm 11。
10. RegExp.escape()
这个功能非常实用。
以前如果你需要把用户输入安全地放进正则表达式:
const keyword = 'a.b*c';
const regex = new RegExp(keyword);
就可能出现问题。
因为:
.
*
+
?
[
]
(
)
这些字符在正则表达式中都有特殊含义。
以前我们经常需要自己写:
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
Node.js 24 对应的现代 JavaScript 环境已经支持:
const regex = new RegExp(
RegExp.escape(keyword)
);
例如:
const keyword = 'a.b*c';
const regex = new RegExp(
RegExp.escape(keyword)
);
这类 API 很适合:
搜索
日志查询
动态过滤
用户输入
规则匹配
11. Float16Array
Node.js 24 的 V8 13.6 引入了:
Float16Array
以前 TypedArray 主要是:
Int8Array
Uint8Array
Int16Array
Uint16Array
Int32Array
Uint32Array
Float32Array
Float64Array
现在增加:
Float16Array
对于普通后端开发来说可能没什么感觉。
但对于:
AI
机器学习
GPU
WebGPU
图像处理
音视频
模型推理
半精度浮点数非常重要。
例如:
const values = new Float16Array([
1.2,
2.4,
3.6
]);
它可以在一些场景下减少数据占用。
这也说明 Node.js 的发展方向已经不只是:
Web 后端。
而是在逐渐向:
通用 JavaScript Runtime
发展。
12. URLPattern
Node.js 24 将:
URLPattern
放到了全局环境。
例如:
const pattern = new URLPattern({
pathname: '/users/:id'
});
const result = pattern.exec(
'https://example.com/users/123'
);
console.log(result.pathname.groups.id);
得到:
123
这对于:
路由
URL 匹配
Middleware
Edge Runtime
Web Framework
都比较有意义。
Node.js 24 官方也将 URLPattern 列为全局 API。
13. AsyncLocalStorage 越来越重要
Node.js 后端开发中,一个非常重要但容易被忽略的能力是:
AsyncLocalStorage
它可以让你在异步调用链中保存上下文。
例如:
HTTP Request
↓
Controller
↓
Service
↓
Repository
↓
Database
我们希望整个链路都能拿到:
requestId
userId
traceId
tenantId
例如:
const storage = new AsyncLocalStorage();
storage.run(
{
requestId: 'abc-123'
},
() => {
service();
}
);
在后面的异步代码中:
const context = storage.getStore();
console.log(context.requestId);
Node.js 24 开始默认使用 AsyncContextFrame 实现 AsyncLocalStorage,这也是 Node.js 在异步上下文性能方面持续优化的一个方向。
对于:
NestJS
日志
链路追踪
OpenTelemetry
微服务
请求上下文
这个能力非常值得掌握。
Node.js 26:Temporal 终于来了
如果让我从 Node.js 18 → 26 里面选一个最值得普通 JavaScript 开发者学习的新 API:
我会选 Temporal。
Node.js 26 于 2026 年 5 月发布,Temporal API 默认开启。
14. Temporal:终于可以认真处理日期和时间了
JavaScript 老牌时间 API:
new Date()
大家用了很多年。
但 Date 有很多历史遗留问题。
比如:
const date = new Date();
console.log(date);
它同时承担了:
日期
时间
时间戳
时区转换
本地时间
UTC
导致处理复杂业务时非常痛苦。
例如:
2026-10-08
到底是:
UTC
还是:
Asia/Shanghai
还是:
Asia/Tokyo
?
Temporal 的设计就是为了把这些概念拆开。
Temporal.PlainDate
如果只需要日期:
const date = Temporal.PlainDate.from(
'2026-10-08'
);
console.log(date);
得到的是:
2026-10-08
它不携带时间,也不携带时区。
非常适合:
生日
节假日
账单日
合同日期
活动日期
Temporal.PlainTime
如果只需要时间:
const time = Temporal.PlainTime.from(
'14:30:00'
);
得到:
14:30:00
Temporal.ZonedDateTime
如果需要真正处理:
日期
+
时间
+
时区
可以使用:
const dateTime =
Temporal.ZonedDateTime.from(
'2026-10-08T14:30:00+08:00[Asia/Shanghai]'
);
这对于:
国际化
预约系统
日历
航班
金融
支付
日志
定时任务
非常重要。
15. Node.js 26 还带来了 Iterator.concat()
Node.js 26 的 V8 14.6 包含:
Iterator.concat()
例如:
const a = [1, 2, 3];
const b = [4, 5, 6];
const result = Iterator.concat(a, b);
console.log([...result]);
得到:
[1, 2, 3, 4, 5, 6]
它和数组:
[...a, ...b]
看起来差不多。
但 Iterator 的优势在于:
可以保持惰性。
对于大量数据:
数据库 Cursor
↓
Iterator
↓
过滤
↓
转换
↓
输出
可以避免一次性把所有数据全部加载到内存。
对于 Node.js 后端的数据处理、日志处理、流处理,这类能力会越来越重要。
Node.js 26 官方已经将 Iterator.concat() 列入 V8 14.6 带来的新能力。
16. Map / WeakMap 的 getOrInsert
Node.js 26 还加入了:
Map.prototype.getOrInsert()
以前我们可能会写:
let value = map.get(key);
if (value === undefined) {
value = createValue();
map.set(key, value);
}
现在可以:
const value = map.getOrInsert(
key,
createValue()
);
还有:
getOrInsertComputed()
可以根据 key 计算默认值。
这种 API 对:
缓存
Memoization
分组
索引
状态管理
非常方便。
17. Node.js 26 开始正式进入 Temporal 时代
如果把 Node.js 18 到 26 的变化压缩一下:
Node.js 18
↓
fetch / Web Streams / Test Runner
Node.js 20
↓
Permission Model / Stable Test Runner
Node.js 22
↓
WebSocket / watch / run / ESM / glob
Node.js 24
↓
URLPattern / Float16Array / RegExp.escape
/ AsyncContextFrame
Node.js 26
↓
Temporal / Iterator.concat
/ Map.getOrInsert
你会发现一个非常明显的趋势:
Node.js 正在不断减少“为了完成基础工作而安装第三方 npm 包”的需求。
Node.js 最大的变化,其实不是 API
如果只看 API,很容易把 Node.js 18 → 26 理解成:
多了几个函数
但实际上变化更深。
Node.js 正在发生三个方向的变化。
第一:Node.js 和浏览器越来越像
以前:
浏览器
├── fetch
├── WebSocket
├── Web Streams
└── Web API
Node.js
├── http
├── https
├── stream
└── 各种 npm package
现在:
浏览器
├── fetch
├── WebSocket
├── Streams
└── Web API
Node.js
├── fetch
├── WebSocket
├── Streams
├── URLPattern
├── Web Crypto
└── 更多 Web API
这意味着未来前后端共享代码会越来越自然。
第二:Node.js 正在吸收以前属于 npm 生态的能力
以前:
HTTP
→ axios
文件匹配
→ glob
开发自动重启
→ nodemon
测试
→ Jest / Mocha
WebSocket
→ ws
现在逐渐变成:
HTTP
→ fetch
文件匹配
→ fs.glob
开发自动重启
→ node --watch
测试
→ node:test
WebSocket
→ WebSocket
当然,这并不是说:
npm 包没用了。
大型项目依然需要:
NestJS
Fastify
Express
TypeORM
Prisma
Zod
Pino
Vitest
...
但 Node.js 自身提供的基础能力越来越丰富。
第三:Node.js 正在变成一个完整的平台
Node.js 早期的定位非常简单:
JavaScript
+
V8
+
事件循环
+
网络 IO
现在已经逐渐变成:
Node.js
┌─────────────────────────────────────────┐
│ JavaScript / V8 │
├─────────────────────────────────────────┤
│ Web APIs │
│ fetch / WebSocket / Streams / Crypto │
├─────────────────────────────────────────┤
│ Module System │
│ CommonJS / ESM │
├─────────────────────────────────────────┤
│ Development │
│ test / watch / run │
├─────────────────────────────────────────┤
│ Security │
│ Permission Model │
├─────────────────────────────────────────┤
│ System │
│ fs / worker / child_process │
├─────────────────────────────────────────┤
│ Modern JavaScript │
│ Temporal / Iterator / TypedArray │
└─────────────────────────────────────────┘
这也是为什么现在学习 Node.js,已经不能只理解成:
“会不会写 Express?”
而应该逐渐理解:
Node.js Runtime 本身提供了什么能力。
Node.js 18 → 26,哪些值得优先学?
如果你是普通 Node.js 后端开发者,我不建议按照版本号一个一个背。
更推荐按照实际价值学习。
第一梯队:现在就应该会
fetch
async/await
AbortController
Web Streams
ESM
node:test
AsyncLocalStorage
node --watch
这些已经和现代 Node.js 开发高度相关。
第二梯队:非常值得掌握
WebSocket
fs.glob
node --run
Permission Model
URLPattern
RegExp.escape
structuredClone
Web Crypto
尤其是:
AsyncLocalStorage
AbortController
Web Streams
对于后端开发非常实用。
第三梯队:未来几年值得关注
Temporal
Iterator
Float16Array
WebAssembly
Explicit Resource Management
Map.getOrInsert()
尤其:
Temporal
值得提前学习。
因为日期时间是后端系统里永远绕不开的问题。
如果你正在维护老 Node.js 项目
这部分可能比新 API 更重要。
如果你的项目还是:
Node 14
Node 16
Node 18
升级时千万不要简单理解成:
nvm install 24
然后:
pnpm install
就结束了。
Node.js 大版本升级真正容易出问题的是:
Node.js
↓
V8
↓
OpenSSL
↓
npm / Corepack
↓
原生模块
↓
TypeScript
↓
依赖包
尤其是老项目。
例如:
nodejieba
sharp
bcrypt
grpc
canvas
sqlite3
这类涉及:
C/C++
Native Addon
node-gyp
预编译二进制
的依赖,非常容易因为 Node.js 大版本升级而出问题。
Node.js 版本升级应该怎么做?
建议按照这个顺序:
1. 确认 Node.js 目标版本
↓
2. 检查 package.json engines
↓
3. 检查原生依赖
↓
4. 更新 npm / pnpm / yarn
↓
5. 删除 node_modules
↓
6. 重新安装依赖
↓
7. TypeScript 编译
↓
8. 单元测试
↓
9. 集成测试
↓
10. Docker 构建
↓
11. 生产环境验证
尤其不要在老项目里直接:
rm -rf node_modules
npm install
然后看到一堆错误就认为:
Node.js 坏了。
很多时候实际上是:
Node.js 升级
↓
V8 / OpenSSL / ABI / npm 变化
↓
旧依赖暴露兼容性问题
Node.js 18 到 26,最值得记住的几个时间点
如果不想记一大堆版本号,只需要记住:
Node 18
↓
fetch
node:test
Node 20
↓
Permission Model
稳定 Test Runner
Node 22
↓
WebSocket
node --watch
node --run
require(ESM)
fs.glob
Node 24
↓
URLPattern
Float16Array
RegExp.escape
AsyncContextFrame
Node 26
↓
Temporal
Iterator.concat
Map.getOrInsert
这张表基本可以代表过去几年 Node.js 的发展方向。
最后
如果把 Node.js 18 → 26 看成一次升级,不如把它理解成一次 Node.js 定位的变化。
早期的 Node.js 更像:
JavaScript Runtime
现在越来越像:
现代 Web Runtime
+
JavaScript Runtime
+
开发工具
+
系统运行时
+
Web API 平台
而且这个趋势还没有结束。
对于现在还在使用 Node.js 16/18 的项目来说,真正值得做的事情并不是为了追版本而升级,而是逐渐理解现代 Node.js 已经提供了哪些能力。
比如以前:
import axios from 'axios';
现在很多场景可以直接:
const response = await fetch(url);
以前:
nodemon app.js
现在:
node --watch app.js
以前:
npm run build
现在也可以:
node --run build
以前:
new Date()
处理复杂日期和时区时经常痛苦不堪。
未来则可以逐渐使用:
Temporal
这可能才是 Node.js 18 → 26 最值得关注的变化:
Node.js 正在从一个“运行 JavaScript 的环境”,逐渐变成一个越来越完整的现代应用运行平台。
而对于今天的新项目来说,与其问:
“Node.js 26 又增加了什么 API?”
不如开始思考:
“以前我安装的那些 npm 包,现在哪些已经可以被 Node.js 原生能力替代?”
这才是这几年 Node.js 演进真正有意思的地方。
