Node.js 21 发布详解:fetch 与 WebStreams 稳定、内置 WebSocket 客户端及性能优化全解析
【免费下载链接】nodejs.orgThe Node.js® Website项目地址: https://gitcode.com/GitHub_Trending/no/nodejs.org
Node.js 21 是于 2023 年 10 月 17 日发布的 "Current" 版本,本次发布将 V8 引擎升级到 11.8,带来了稳定化的fetch与WebStreams、实验性内置 WebSocket 客户端、可切换模块默认类型的--experimental-default-type标志、测试运行器对 glob 表达式的支持,以及 streams/HTTP 等关键路径上的性能优化。本文以 v21 发布公告 为主体,结合 nodejs.org 网站仓库中与发布数据、下载与发布帖生成相关的源码实现,逐项解读这些新特性及其背后的实现与使用方式。
版本路线:从 Current 到 LTS 的 6 个月周期
Node.js 21 发布时处于 "Current" 状态,它将取代当时的 Node.js 20 成为新的 Current 版本线。按照官方发布节奏,Node.js 20 于当月晚些时候进入长期支持(LTS),而 Node.js 21 则以 Current 状态运行 6 个月,直到 2024 年 4 月。
在 nodejs.org 仓库中,这种版本状态的判定逻辑被固化在发布数据生成器中。releaseData.mjs 中的getNodeReleaseStatus函数按以下优先级决定每个大版本的状态:
- 若已到达 EOL 日期(
support.phases.dates.end),状态为EOL; - 若最新版本处于 LTS(
latest.lts.isLts为真),状态为LTS; - 否则状态为
Current。
const getNodeReleaseStatus = (latest, eol) => { const now = new Date(); if (eol && now >= new Date(eol)) { return 'EOL'; } if (latest.lts.isLts) { return 'LTS'; } return 'Current'; };这段源码清晰地体现了 Node.js 版本线的三种状态划分,也解释了发布公告中"Node.js 21 将在未来 6 个月保持 Current"这一表述的技术依据。这些数据最终被下载页、版本选择器(Release.VersionDropdown)以及各个表格组件消费,用于在网站上动态展示当前版本状态。
fetch 与 WebStreams 标记为稳定
Node.js 21 的一个重要变化是将fetch模块以及WebStreams从实验性状态提升为稳定。这意味着WebStreams、FormData、Headers、Request、Response和fetch这组 Web 标准 API 在 Node.js 中不再是实验性功能,可以直接在生产环境中依赖它们而不必担心 API 变更。
这一变化的直接体现是:你不再需要任何实验性标志,就可以在服务端代码中像浏览器一样使用fetch发起 HTTP 请求、处理FormData表单数据,或借助WebStreams以流式方式读写数据。对于在 Node.js 中编写 HTTP 客户端、下载代理或数据管道类应用而言,这大幅降低了对外部依赖的需求。
值得注意的是,Node.js 的fetch实现基于 undici 库。在随后的 v21.0.0 详细发布帖 v21.0.0.md 中可以看到 undici 被更新到 5.25.4,为fetch的稳定性提供了底层支撑。
实验性内置 WebSocket 客户端
Node.js 21 引入了一个浏览器兼容的WebSocket实现,但该功能仍处于实验阶段,需要通过标志启用:
node --experimental-websocket app.js启用后,你可以在 Node.js 中直接使用标准 WebSocket API 建立与服务器之间的双向通信连接,而无需安装第三方库。作为实验性功能,其 API 在未来版本中仍可能发生变化,因此生产环境使用前需要关注后续版本的变更公告。
V8 11.8:性能与新语言特性
Node.js 21 内置的 V8 引擎升级到了 11.8(对应 Chromium 118),带来了性能提升和以下新语言特性:
- Array grouping:
Object.groupBy/Map.groupBy,允许按回调返回的键对数组元素进行分组; ArrayBuffer.prototype.transfer:支持将ArrayBuffer的所有权转移给新对象,避免数据拷贝,是结构化克隆与共享内存场景下的重要能力;- WebAssembly extended-const 表达式:允许在常量表达式中执行更复杂的操作(如全局变量读取、算术运算),为 WebAssembly 编译器生成更灵活的常量提供了支持。
V8 引擎的升级同时带来了模块 ABI 版本的变化:NODE_MODULE_VERSION提升到 120,这意味着针对旧版本 Node.js 编译的原生模块(N-API 以外的直接绑定 V8 API 的 addon)需要重新编译才能用于 Node.js 21。
测试运行器:glob 表达式支持
Node.js 内置测试运行器(test runner)在本次更新中引入了对 glob 表达式的支持。此前通过--test参数指定测试文件时能力有限,现在你可以使用强大的 glob 模式灵活地选择测试文件集合。例如,跨多个目录运行所有.test.js后缀的测试文件:
node --test **/*.test.js这种模式让 CI 配置和本地测试执行更加灵活,可以按目录、按命名模式或按扩展名精细控制哪些文件参与测试。发布公告中这一特性由 #47653 引入,同批提交还包含了fs.globSync实现,为文件系统级的 glob 匹配提供了原生支持。
nodejs.org 仓库自身也大量使用 Node.js 测试生态:例如 releaseData.test.mjs、vulnerabilities.test.mjs 等测试文件覆盖了发布数据与漏洞数据的生成逻辑,展示了测试运行器在真实项目中的典型用法。
ESM 模块默认类型切换:--experimental-default-type
Node.js 21 新增了实验性标志--experimental-default-type,用于翻转 Node.js 对"模糊代码"(ambiguous code)的默认模块解释方式。
所谓"模糊代码",是指那些既没有被package.json的"type"字段显式声明、也没有通过.mjs/.cjs扩展名或--input-type标志明确类型的输入。这些输入默认按 CommonJS 处理。当传入--experimental-default-type=module时,以下三类输入会被改按 ES modules 解释:
- 通过
--eval或 STDIN 提供的字符串输入(当--input-type未指定时); - 以
.js结尾或没有扩展名的文件——前提是同一目录或任何父目录中不存在package.json; - 以
.js结尾或没有扩展名的文件——当最近的父级package.json缺少type字段时(但node_modules目录内的文件除外)。
此外,还有一个交互细节:如果同时传入--experimental-wasm-modules,且无扩展名文件以 WebAssembly 魔数\0asm开头,则该文件会被解释为 WebAssembly 模块。
发布公告同时提到,Node.js 团队还在探索基于 ES 模块语法检测来自动判断文件模块类型的方式,目标是最终能以最小的破坏性变更让 ES 模块语法默认可用。对于正在迁移到 ESM 的项目,这个标志提供了在不动package.json的情况下先行验证"默认 ESM"行为的手段,但需要注意它仍是实验性的。
模块自定义钩子:globalPreload移除,改用register与initialize
Node.js 21 移除了模块自定义钩子(module customization hooks)中的globalPreload。旧机制通过globalPreload在钩子线程与应用线程之间传递数据,新方案将其职责拆分为两个部分:
register:用于从应用线程向自定义钩子发送数据;initialize:用于在线程之间建立通信通道。
这一改动是为了让模块自定义钩子的线程模型更清晰、更安全。使用自定义加载器(loader)的项目需要从旧的globalPreload写法迁移到新的register+initialize组合。
fs.writeFile 新增flush选项
文件写入时,数据并不总是立即刷入持久化存储,后续读操作可能因此读到旧数据(stale data)。Node.js 21 为fs.writeFile系列函数(fs.writeFile、fs.writeFileSync、fs.promises.writeFile)新增了'flush'选项,在成功写入结束后强制将数据刷新到永久存储:
import { writeFileSync } from 'node:fs'; writeFileSync('data.txt', 'hello', { flush: true });这对于日志系统、数据库快照、崩溃恢复等对数据持久性有强要求的场景非常实用,可以避免"写入成功但断电后数据丢失"的隐患。
性能优化:Streams 与 HTTP chunked 响应
性能团队在 URL、fetch、streams、node:fs 和 HTTP 等模块上做了持续优化,本次版本中有两个代表性改进。
Streams 优化
streams 维护者通过以下手段进一步优化了 Writable 和 Readable 流的性能:
- 移除冗余检查;
- 利用位图(bitmap)替代部分状态标志;
- 以更高效的方式调度回调。
这些改动减少了每次数据块处理时的开销,对高频读写场景有明显收益。
HTTP 分块响应的合并
在此之前,对 chunked 响应调用多次.write(...)时,Node.js 会为每次调用生成独立的分块,无论响应是否处于 corked 状态,这给客户端和服务端都带来了不必要的开销。
以 Transfer-Encoding 分块编码 文档中的经典示例为例:
res.cork(); res.write('Mozilla'); res.write(' Developer Network'); res.uncork();按照分块编码协议,每个块需要以十六进制长度开头、跟随\r\n、再跟随块内容与\r\n,终止块是长度为 0 的普通块。旧行为会生成两个数据块:
HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked 7\r\n Mozilla\r\n 18\r\n Developer Network\r\n 0\r\n \r\n本次优化后,在uncork()时所有write(...)调用被合并为单个数据块,绕开了大量不必要的开销:
HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked 25\r\n Mozilla Developer Network\r\n 0\r\n \r\n在 v21.0.0.md 的提交列表中,该改动标记为(SEMVER-MAJOR)级别的 http 变更,意味着依赖"每次 write 生成独立 chunk"这一旧行为的代码需要留意行为差异。这一优化也是本次版本中对外可见的性能收益之一。
llhttp 9.1.2:HTTP 解析严格模式全面开启
Node.js 21 将 llhttp 升级到 9.1.2,并默认启用此前仅在严格模式下生效的解析规则,提升代码可靠性与安全性。主要变化包括:
- 此前未默认启用的严格模式设置,现在全部默认开启;
- header 之后必须存在
\r\n(此前允许单独的\r),chunk 之后同样必须跟\r\n,保证数据处理的一致性; - 解析到携带
Connection: close头的消息后,不再允许继续传输数据,以增强协议遵守度与连接处理能力。
如果某些特殊场景需要兼容旧的解析行为,可以通过--insecure-http-parser标志关闭上述变更。发布公告建议开发者审查自己的代码库并相应调整实现,确保与新版解析器无缝集成。
navigator 全局对象
Node.js 21 引入了全局navigator对象,增强与 Web 平台的互操作性。目前开发者可以通过它访问硬件并发信息:
console.log(navigator.hardwareConcurrency);该属性返回可用于并行任务的逻辑处理器核心数,可用于动态调整 Worker 线程池大小、并发任务数等场景,是实现"按机器能力自适应并发"的便捷手段。作为全局对象,它无需require或import即可直接使用。
弃用与破坏性变更
本次发布包含两项运行时弃用(runtime deprecation),均为 SEMVER-MAJOR 级别:
- punycode 运行时弃用:
node:punycode模块在运行时被标记为弃用(此前已处于文档级弃用状态),调用时会产生弃用警告; util.promisify包装返回 Promise 的函数时运行时弃用:对本身已返回 Promise 的函数再执行promisify会在运行时发出弃用警告。
完整的 SEMVER-MAJOR 提交清单可在 v21.0.0.md 中查看,其中包括构建工具链要求的提升(如放弃 Visual Studio 2019 支持、上调 macOS/Xcode 最低版本)、ICU 最低版本提升到 73、events.on/events.once的选项校验、net.server.maxConnections=0语义修正等。若你的项目或依赖使用了被影响的能力,升级到 Node.js 21 前应重点关注这些变更。
如何获取与验证 Node.js 21
你可以通过 下载页 或 Current 版本页 获取 Node.js 21 的安装包、二进制文件与源码。
nodejs.org 的下载页会根据用户操作系统、架构与安装方式动态生成下载链接,其 URL 生成逻辑集中在 url.ts 的getNodeDownloadUrl函数中,例如 Linux x64 二进制对应node-v21.0.0-linux-x64.tar.xz,macOS Apple Silicon 对应node-v21.0.0-darwin-arm64.tar.gz。发布帖生成脚本 release-post/index.mjs 会从 changelog、SHASUMS、下载清单等数据源自动拼装每个版本的发布帖,downloadsTable.mjs 中维护了各平台安装包/二进制/源码的完整命名清单;同时,downloadSnippets.mjs 负责收集各语言版本(如snippets/en/download/下的 bash 片段)的安装命令,供下载页的代码框展示。你可以参照下载页提供的官方命令或访问 下载归档 查找历史版本。
升级建议
发布公告同时提醒:Node.js 16(LTS)已到达 End-of-Life,建议仍停留在 Node.js 16 的项目尽早规划升级到 Node.js 18(LTS)或 Node.js 20(LTS)。如果你的项目希望体验最新特性,可以直接试用 Node.js 21,并在测试环境验证应用与模块的兼容性——这将有助于确保你的项目与 Node.js 最新的变更和特性保持兼容。
【免费下载链接】nodejs.orgThe Node.js® Website项目地址: https://gitcode.com/GitHub_Trending/no/nodejs.org
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考