Node.js Streams 和 Web Streams API 对比
Node.js 流与 Web 标准流并存的时代,该用哪个?本文梳理两套 API 的历史、核心差异、互转方法与选型建议。
Node.js 服务端代码用 Node 流(生态成熟、Node API 原生);跨运行时代码(浏览器/边缘函数/Node 复用)用 Web Streams。Node 18+ 内置互转(Readable.toWeb/fromWeb),两套体系不是二选一而是按需桥接。
两套流的历史
Node.js Streams(2010 年起):Node 原创的流体系,EventEmitter 驱动,pipe/pipeline API——Node 全部 IO API 的底座。
Web Streams(WHATWG 标准):浏览器标准化组织制定的流规范,Promise 驱动,fetch 的 response.body 就是它——Node 18 起内置实现。
API 风格对比
// Node 流:事件 + pipe
const nodeReadable = fs.createReadStream('file.txt');
nodeReadable.pipe(transform).pipe(fs.createWriteStream('out.txt'));
// Web 流:Promise + 异步迭代
const webStream = (await fetch(url)).body;
await webStream.pipeThrough(new TextDecoderStream()).pipeTo(writable);
// Web 流还可以直接迭代
for await (const chunk of webStream) { /* ... */ }
核心差异
| 维度 | Node 流 | Web 流 |
|---|---|---|
| 驱动方式 | EventEmitter | Promise |
| 背压处理 | 自动(pipe) | 自动 + 显式策略(QueuingStrategy) |
| 生态 | Node API 全面原生 | 浏览器/fetch/边缘运行时 |
| 取消语义 | destroy() | AbortController 标准集成 |
互转:桥接两个世界
const { Readable } = require('node:stream');
// Node 流 → Web 流
const webStream = Readable.toWeb(fs.createReadStream('file.txt'));
// Web 流 → Node 流
const nodeStream = Readable.fromWeb((await fetch(url)).body);
fetch() 返回 Web 流、fs 返回 Node 流——混用时互转抹平差异。
选型建议
- 纯 Node 服务端:Node 流——所有内置 API(fs、http、net、zlib)原生产出,生态中间件全兼容;
- 边缘函数/Cloudflare Workers/Deno:只有 Web 流可用;
- 同构库(Node 与浏览器都要跑):基于 Web 流开发,Node 侧 toWeb/fromWeb 适配;
- 新业务无历史包袱:都可,跟团队已有代码保持一致即可。
常见问题(FAQ)
Q:性能有差异吗?
微基准互有胜负且差距不大;Node 流在高吞吐服务端场景优化历史更长。性能从不是两套 API 的选择依据——运行环境兼容性才是。
Q:新写的 Transform 该实现哪个接口?
服务端内部用 Node 的 Transform;要暴露给浏览器/边缘复用的用 Web 的 TransformStream。或者写纯函数 + 两侧各包一层薄适配。
Q:pipeline 有 Web 版吗?
Web 流的 pipeThrough/pipeTo 链自带错误传播(Promise rejection 贯穿全链),语义等价于 Node 的 pipeline——错误处理两者都已内建。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。