Undici 完全指南:Node.js 从零实现的 HTTP/1.1 客户端与 Fetch 引擎

原创2026-09-26 13:52:411,358 阅读
文章标签:后端网络通信

Undici 完全指南:Node.js 从零实现的 HTTP/1.1 客户端与 Fetch 引擎

Undici 是 Node.js 生态中一个从零开始编写的 HTTP/1.1 客户端,同时为 Node.js v18+ 内置的 fetch() 提供底层实现。本文以仓库 README.md 为骨架,结合 index.js 等源码与 docs 文档,系统讲解 Undici 的安装、基准测试、内置 fetch 对比、常用 API、缓存拦截器、全局安装、Body Mixins、规范符合性与已知工作区,帮助你判断何时该用它、如何正确使用它。

Undici 在意大利语中意为"十一":1.1 → 11 → Eleven → Undici。这也是美剧《怪奇物语》的彩蛋。

安装与版本背景

Undici 是一个独立的 npm 包,安装方式与普通依赖一致:

npm i undici

当前仓库的 package.json 中 engines 声明要求 Node.js >=22.19.0(对应 8.x 主线)。与此同时,Node.js 自身从 v18 起就内置了一个捆绑版本的 Undici,你随时可以通过 process.versions.undici 查看当前 Node.js 内置的 Undici 版本:

console.log(process.versions.undici); // 例如 "5.28.4"

两者的关键区别是:内置 fetch 受限于你 Node.js 版本所捆绑的 Undici 版本,而独立安装 Undici 模块则能使用最新特性、Bug 修复与高级 API。这一点在本文"Undici vs 内置 Fetch"一节还会详细展开。

基准测试:Undici 各 API 的性能层级

README 中提供了官方基准测试数据,测试脚本分别是 benchmarks/benchmark.js(HTTP/1.1)、benchmarks/benchmark-https.js(HTTP/1.1 over TLS)和 benchmarks/benchmark-http2.js(HTTP/2)。测试场景为 50 个 TCP 连接、pipelining 深度 10,运行于 Node 24.14.1。

HTTP/1.1 基准

Tests Samples Result Tolerance Difference with slowest
node-fetch 50 4711.86 req/sec ± 2.92 % -
undici - fetch 75 5438.50 req/sec ± 2.97 % + 15.42 %
axios 45 5448.08 req/sec ± 2.98 % + 15.62 %
request 65 5809.63 req/sec ± 2.90 % + 23.30 %
http - no keepalive 35 5910.77 req/sec ± 2.87 % + 25.44 %
got 50 6047.80 req/sec ± 2.91 % + 28.35 %
superagent 60 7534.53 req/sec ± 2.97 % + 59.91 %
http - keepalive 75 9343.41 req/sec ± 2.90 % + 98.30 %
undici - pipeline 65 13470.70 req/sec ± 2.93 % + 185.89 %
undici - request 80 16850.87 req/sec ± 2.93 % + 257.63 %
undici - stream 101 18488.56 req/sec ± 3.81 % + 292.38 %
undici - dispatch 101 20786.44 req/sec ± 3.08 % + 341.15 %

HTTP/1.1 over HTTPS 基准

Tests Samples Result Tolerance Difference with slowest
https - no keepalive 10 1358.40 req/sec ± 1.99 % -
undici - fetch 30 3721.76 req/sec ± 2.97 % + 173.98 %
https - keepalive 35 5633.91 req/sec ± 2.84 % + 314.75 %
undici - pipeline 15 6254.05 req/sec ± 2.80 % + 360.40 %
undici - request 25 6669.80 req/sec ± 2.73 % + 391.01 %
undici - stream 25 7019.04 req/sec ± 2.77 % + 416.71 %
undici - dispatch 20 7361.85 req/sec ± 2.90 % + 441.95 %

HTTP/2 基准

Tests Samples Result Tolerance Difference with slowest
undici - fetch 45 3499.03 req/sec ± 2.93 % -
native - http2 25 4904.58 req/sec ± 2.81 % + 40.17 %
undici - pipeline 60 5836.82 req/sec ± 2.99 % + 66.81 %
undici - request 65 6831.25 req/sec ± 2.83 % + 95.23 %
undici - stream 55 6874.30 req/sec ± 2.91 % + 96.46 %
undici - dispatch 55 7791.23 req/sec ± 2.96 % + 122.67 %

从三组数据可以清晰看出一个稳定的性能层级:undici.dispatch > undici.stream > undici.request > undici.pipeline > undici.fetch。这正是 README 中"Performance Comparison"一节结论的来源——直接使用底层 Dispatcher API(dispatch)比走 Fetch 标准封装的开销更低。需要说明的是:这些数据来自当前仓库 README 记录的固定测试环境,实际数据会随 Node 版本、机器与网络条件变化,应作为相对性能层级参考而非绝对结论。

Undici vs 内置 Fetch:如何选择

Node.js 从 v18 开始内置了由 Undici 驱动的 fetch(),但它与独立安装的 Undici 模块在使用体验和能力边界上差异显著。

内置 Fetch(Node.js v18+)

// Node.js v18+ 中全局可用
const response = await fetch('https://api.example.com/data');
const data = await response.json();

// 查看内置的 undici 版本
console.log(process.versions.undici); // 例如 "5.28.4"

优点:

  • 零额外依赖;
  • 可在不同 JS 运行时(浏览器 + Node.js)间编写 isomorphic 代码;
  • 自动处理 gzip、deflate、br 等压缩;
  • 内置缓存支持(开发中)。

缺点:

  • 只能使用你 Node.js 版本捆绑的那个 Undici 版本;
  • 对连接池和高级特性的控制能力较弱;
  • 错误处理遵循 Web API 标准,错误被包装成 TypeError,可读性有限;
  • Web Streams 实现带来额外性能开销。

独立 Undici 模块

npm install undici
import { request, fetch, Agent, setGlobalDispatcher } from 'undici';

// 追求极致性能用 undici.request
const { statusCode, headers, body } = await request('https://api.example.com/data');
const data = await body.json();

// 或用 undici.fetch 配合自定义配置
const agent = new Agent({ keepAliveTimeout: 10000 });
setGlobalDispatcher(agent);
const response = await fetch('https://api.example.com/data');

优点:

  • 最新的 Undici 特性与 Bug 修复;
  • 可访问高级 API(request、stream、pipeline);
  • 对连接池进行细粒度控制;
  • 错误信息更清晰、更易调试;
  • 尤其是 undici.request 路径下性能显著更优;
  • 支持 HTTP/1.1 pipelining;
  • 自定义拦截器与中间件(如 interceptors.cache、interceptors.retry、interceptors.redirect,见 index.js);
  • 提供 ProxyAgent、Socks5ProxyAgent、EnvHttpProxyAgent、MockAgent、RetryAgent、H2CClient 等高级组件。

缺点:

  • 多一个需要管理的依赖;
  • 打包体积更大。

何时使用内置 Fetch

  • 追求零依赖;
  • 需要编写浏览器与 Node.js 通用的 isomorphic 代码;
  • 发布 npm 包,希望最大化与各 JS 运行时兼容;
  • 只是简单 HTTP 请求、不需要高级配置;
  • 不依赖某个特定 Undici 版本的特性。

何时使用独立 Undici 模块

  • 需要最新的 Undici 特性与性能改进;
  • 需要高级连接池配置;
  • 需要内置 fetch 没有的 API(ProxyAgent、Socks5ProxyAgent、MockAgent 等);
  • 性能敏感场景(用 undici.request 获得最高速度);
  • 需要更好的错误处理与调试能力;
  • 需要 HTTP/1.1 pipelining 或高级拦截器;
  • 希望协议层与 API 层解耦。

性能对比结论

基于上述基准,典型性能排序为:

  1. undici.request() —— 最快、最省资源;
  2. undici.fetch() —— 性能良好、标准合规;
  3. Node.js http/https —— 基线水平。

迁移指南:从内置 fetch 迁移到 Undici

// 迁移前:内置 fetch
const response = await fetch('https://api.example.com/data');

// 迁移后:Undici fetch(可直接替换)
import { fetch } from 'undici';
const response = await fetch('https://api.example.com/data');

// 或:Undici request(性能更好)
import { request } from 'undici';
const { statusCode, body } = await request('https://api.example.com/data');
const data = await body.json();

保持 fetch 与 FormData 同源

当使用 FormData 作为请求体时,务必让 fetch 与 FormData 来自同一个实现,否则二者内部的 multipart 序列化与标识符(boundary)逻辑可能不匹配。推荐以下任一模式:

// 模式一:全部使用内置全局对象
const body = new FormData()
body.set('name', 'some')
await fetch('https://example.com', {
  method: 'POST',
  body
})
// 模式二:全部从 undici 导入
import { fetch, FormData } from 'undici'

const body = new FormData()
body.set('name', 'some')
await fetch('https://example.com', {
  method: 'POST',
  body
})

如果希望安装的 undici 包直接提供这些全局对象,可以先调用 install():

import { install } from 'undici'

install()

const body = new FormData()
body.set('name', 'some')
await fetch('https://example.com', {
  method: 'POST',
  body
})

install() 会用 Undici 的实现替换全局的 fetch、Headers、Response、Request、FormData,同时还会安装 Undici 的 WebSocket、CloseEvent、ErrorEvent、MessageEvent 与 EventSource 全局对象(对应实现见 index.js 与 lib/global.js)。

要避免的反模式:混用全局 FormData + undici.fetch(),或者 undici.FormData + 内置全局 fetch()。

版本兼容性

console.log(process.versions.undici);

安装独立模块可以脱离 Node.js 捆绑版本的限制,使用更新的 Undici,从而获得最新特性与性能改进。仓库 README 的 Long Term Support 一节给出了各主版本的 Node.js 支持范围(详见本文"长期支持"一节)。

快速开始

基础请求

import { request } from 'undici'

const {
  statusCode,
  headers,
  trailers,
  body
} = await request('http://localhost:3000/foo')

console.log('response received', statusCode)
console.log('headers', headers)

for await (const data of body) { console.log('data', data) }

console.log('trailers', trailers)

undici.request 走的是底层 Dispatcher 请求路径。从源码看,lib/api/api-request.js 中的 request(opts, callback) 在未提供回调时返回 Promise,内部通过 RequestHandler(一个继承自 AsyncResource 的处理器)把响应包装成可异步迭代的 Readable,并在 onResponseStart 中透出 statusCode、headers、trailers、body 等字段。request 的默认方法语义是:有 body 时为 PUT,否则为 GET(见 index.js 中 makeDispatcher 的实现)。

使用缓存拦截器(Cache Interceptor)

Undici 内置了遵循 HTTP 缓存最佳实践的拦截器,可以按 Cache-Control / Expires 策略自动缓存 GET/HEAD 响应:

import { fetch, Agent, interceptors, cacheStores } from 'undici';

// 创建带缓存拦截器的客户端
const client = new Agent().compose(interceptors.cache({
  // 可选:配置缓存存储(默认为 MemoryCacheStore)
  store: new cacheStores.MemoryCacheStore({
    maxSize: 100 * 1024 * 1024, // 100MB
    maxCount: 1000,
    maxEntrySize: 5 * 1024 * 1024 // 5MB
  }),

  // 可选:指定缓存哪些 HTTP 方法(默认 ['GET', 'HEAD'])
  methods: ['GET', 'HEAD']
}));

// 设置为全局 dispatcher,之后所有 fetch 都走缓存
setGlobalDispatcher(client);

async function getData() {
  const response = await fetch('https://api.example.com/data');
  // 服务端应返回合适的 Cache-Control 响应头,缓存策略会据此生效
  return response.json();
}

// 第一次请求 —— 回源
const data1 = await getData();

// 第二次请求 —— 若仍在 max-age 内则直接命中缓存
const data2 = await getData();

关键特性:

  • 自动缓存:遵循 Cache-Control 与 Expires 响应头;
  • 校验(Validation):支持 ETag 与 Last-Modified 条件请求;
  • 存储选项:内存(MemoryCacheStore)或持久化 SQLite(SqliteCacheStore)两种后端;
  • 灵活配置:可配置缓存大小、TTL 等。

从源码看,lib/interceptor/cache.js 实现了完整的缓存语义:needsRevalidation 会对带 no-cache 请求指令、未限定的 no-cache 响应指令以及携带 if-modified-since / if-none-match 条件头的请求强制走重新校验;staleResponseRequiresRevalidation 则处理 must-revalidate、共享缓存下的 proxy-revalidate 与 s-maxage 语义。默认的 lib/cache/memory-cache-store.js 内部默认值为 maxCount: 1024、maxSize: 104857600(100MB)、maxEntrySize: 5242880(5MB),超出上限时会触发 maxSizeExceeded 事件并淘汰旧条目;README 示例中的 maxCount: 1000 是显式自定义值。缓存相关 API 在 index.js 中通过 cacheStores.MemoryCacheStore / cacheStores.SqliteCacheStore 导出。

全局安装(install)

Undici 提供 install() 函数,把 fetch 相关及 Web API 类挂到 globalThis 上:

import { install } from 'undici'

install()

// 现在可以不导入直接全局使用
const response = await fetch('https://api.example.com/data')
const data = await response.json()

const headers = new Headers([['content-type', 'application/json']])
const request = new Request('https://example.com')
const formData = new FormData()
const ws = new WebSocket('wss://example.com')
const eventSource = new EventSource('https://example.com/events')

install() 会向 globalThis 添加以下类:

  • fetch —— fetch 函数
  • Headers —— HTTP 头管理
  • Response —— HTTP 响应表示
  • Request —— HTTP 请求表示
  • FormData —— 表单数据处理
  • WebSocket —— WebSocket 客户端
  • CloseEvent、ErrorEvent、MessageEvent —— WebSocket 事件
  • EventSource —— Server-Sent Events 客户端

调用 install() 后,这些全局对象全部来自同一个 Undici 实现(例如全局 fetch 与全局 FormData 都是 Undici 版本),WebSocket 与 EventSource 亦然。如果你希望通过全局对象使用 Undici,这是官方推荐的配置方式。源码层面,index.js 中的 install() 逐一将这些导出赋值到 globalThis,而 lib/global.js 维护了全局 dispatcher 的注册逻辑。

适用场景:

  • 为没有 fetch 的环境做 polyfill;
  • 保证不同 Node.js 版本之间 fetch 行为一致;
  • 让 Undici 的实现对依赖全局对象的库全局可用。

Body Mixins:响应体格式化

body mixin 是最常用的请求/响应体消费方式,包括:

  • .arrayBuffer()
  • .blob()
  • .bytes()
  • .json()
  • .text()

[!NOTE] undici.request 返回的 body 不实现 .formData()(只有 Fetch 标准的 Request / Response 才支持)。

[!WARNING] .arrayBuffer()、.blob()、.bytes()、.json()、.text()、.formData() 这些 mixin 都会先把整个响应体缓冲到内存,再(视情况)解码或解析并保留该表示。因此使用它们意味着你信任响应体足够小、能放进可用内存。不要对来自不可信或用户可控来源的响应使用这些方法。此时应把响应体当作流来消费,并施加应用层的大小限制:fetch 响应使用 response.body,undici.request() 则使用其返回的 body。需要流式解码文本时,可在 fetch 的 Request / Response 上使用 body.textStream()。

示例:

import { request } from 'undici'

const {
  statusCode,
  headers,
  trailers,
  body
} = await request('http://localhost:3000/foo')

console.log('response received', statusCode)
console.log('headers', headers)
console.log('data', await body.json())
console.log('trailers', trailers)

注意:一旦调用过某个 mixin,body 就不能再被复用——例如依次调用 .body.json() 与 .body.text() 会抛错 TypeError: unusable(通过 Promise rejection 返回)。如果需要在调用 mixin 之后读取纯文本 body,最佳实践是先调用 .text() mixin,再手动把文本解析成目标格式。

常用 API 方法

本节整理 README 中最常用的顶层 API。对于这些顶层 API,url 参数提供请求的 origin 与 path,不要再在第二个 options 参数里传 origin 或 path(Dispatcher 选项类型包含这些字段,是因为 dispatcher 方法是更底层的 API,本身不接收独立的 url 参数)。

undici.request([url, options]): Promise

参数:

  • url string | URL | UrlObject
  • options RequestOptions
    • dispatcher Dispatcher —— 默认 getGlobalDispatcher
    • method String —— 默认:有 options.body 时为 PUT,否则为 GET

返回 Dispatcher.request 方法的 Promise 结果,内部等价于调用 options.dispatcher.request(options)。更多细节见 Dispatcher.request,完整示例见 docs/examples/README.md。

undici.stream([url, options, ]factory): Promise

参数:

  • url string | URL | UrlObject
  • options StreamOptions
    • dispatcher Dispatcher —— 默认 getGlobalDispatcher
    • method String —— 默认:有 options.body 时为 PUT,否则为 GET
  • factory Dispatcher.stream.factory

返回 Dispatcher.stream 方法的 Promise 结果,内部调用 options.dispatcher.stream(options, factory)。

undici.pipeline([url, options, ]handler): Duplex

参数:

  • url string | URL | UrlObject
  • options PipelineOptions
    • dispatcher Dispatcher —— 默认 getGlobalDispatcher
    • method String —— 默认:有 options.body 时为 PUT,否则为 GET
  • handler Dispatcher.pipeline.handler

返回:stream.Duplex,内部调用 options.dispatcher.pipeline(options, handler)。pipeline 适合在响应尚未结束时就开始向客户端转发(如代理场景),是 HTTP/1.1 基准中吞吐量高于 fetch 的重要原因。

undici.connect([url, options]): Promise

使用 HTTP CONNECT 与目标资源建立双向通信。

参数:

  • url string | URL | UrlObject
  • options ConnectOptions
  • callback (err: Error | null, data: ConnectData | null) => void(可选)

返回 Dispatcher.connect 方法的 Promise 结果,内部调用 options.dispatcher.connect(options)。

undici.fetch(input[, init]): Promise

实现 Fetch 标准。基础用法:

import { fetch } from 'undici'

const res = await fetch('https://example.com')
const json = await res.json()
console.log(json)

还可以给 fetch 传一个可选的 dispatcher:

import { fetch, Agent } from 'undici'

const res = await fetch('https://example.com', {
  // MockAgent 也支持
  dispatcher: new Agent({
    keepAliveTimeout: 10,
    keepAliveMaxTimeout: 10
  })
})
const json = await res.json()
console.log(json)

关于 dispatcher 与连接池配置(keepAliveTimeout、keepAliveMaxTimeout、connections、pipelining、allowH2、maxConcurrentStreams 等),可查阅 docs/docs/api/Agent.md 与 docs/docs/api/Pool.md。Agent 会按 origin 懒创建并复用 Pool(连接数为 1 时退化为 Client),空闲 dispatcher 在无打开连接且不忙碌时自动关闭。

request.body 支持的 body 类型

  • ArrayBuffer
  • ArrayBufferView
  • AsyncIterables
  • Blob
  • Iterables
  • String
  • URLSearchParams
  • FormData

本实现中 request.body 额外接受 Async Iterables(这是 Fetch 标准中尚不存在的能力):

import { fetch } from 'undici'

const data = {
  async *[Symbol.asyncIterator]() {
    yield 'hello'
    yield 'world'
  },
}

await fetch('https://example.com', { body: data, method: 'POST', duplex: 'half' })

FormData 除文本与 Buffer 外,还可以通过 Blob 对象使用流:

import { openAsBlob } from 'node:fs'

const file = await openAsBlob('./big.csv')
const body = new FormData()
body.set('file', file, 'big.csv')

await fetch('http://example.com', { method: 'POST', body })

request.duplex

  • 'half'

当 request.body 是 ReadableStream 或 Async Iterables 时必须设置 duplex。尽管取值必须是 'half',本实现实际是 full duplex(请求体与响应体可以同时双向读写)。

response.body

Node.js 有两种流:遵循浏览器 WHATWG 标准的 Web Streams,以及较老的 Node 专属 Streams API。response.body 返回的是可读 Web Stream。如果更愿意使用 Node stream,可以用 .fromWeb() 转换:

import { fetch } from 'undici'
import { Readable } from 'node:stream'

const response = await fetch('https://example.com')
const readableWebStream = response.body
const readableNodeStream = Readable.fromWeb(readableWebStream)

undici.upgrade([url, options]): Promise

升级到不同协议(如 WebSocket 的 HTTP Upgrade 握手)。

参数:

  • url string | URL | UrlObject
  • options UpgradeOptions
  • callback (error: Error | null, data: UpgradeData) => void(可选)

返回 Dispatcher.upgrade 方法的 Promise 结果,内部调用 options.dispatcher.upgrade(options)。

undici.setGlobalDispatcher(dispatcher)

  • dispatcher Dispatcher

设置 Common API Methods 使用的全局 dispatcher。全局 dispatcher 在兼容的 Undici 模块之间共享,包括 Node.js 内部捆绑的 Undici。Undici 将该 dispatcher 存放在 Symbol.for('undici.globalDispatcher.2') 下;setGlobalDispatcher() 还会用 Dispatcher1Wrapper 把配置镜像到 Symbol.for('undici.globalDispatcher.1'),使 Node.js 内置 fetch 能继续使用旧的 handler 契约、而 Undici 走新的 handler API。源码见 lib/global.js。如果传入的对象没有 dispatch 方法,会抛出 InvalidArgumentError("Argument agent must implement Agent");当 globalThis 不可扩展(如被冻结)时,会回退到模块内部的 fallback 存储。

undici.getGlobalDispatcher()

获取 Common API Methods 使用的全局 dispatcher,返回:Dispatcher。

undici.setGlobalOrigin(origin)

  • origin string | URL | undefined

设置 fetch 中使用的全局 origin。传入 undefined 会重置全局 origin,此后 Response.redirect、new Request() 与 fetch 在收到相对路径时会抛错:

setGlobalOrigin('http://localhost:3000')

const response = await fetch('/api/ping')

console.log(response.url) // http://localhost:3000/api/ping

undici.getGlobalOrigin()

获取 fetch 中使用的全局 origin,返回:URL。

UrlObject

  • port string | number(可选)
  • path string(可选)
  • pathname string(可选)
  • hostname string(可选)
  • origin string(可选)
  • protocol string(可选)
  • search string(可选)

规范符合性:Undici 与 Fetch / HTTP/1.1 标准的差异

本节记录 HTTP/1.1 与 Fetch 标准中 Undici 不支持或未完全实现的部分,属于 README 明确声明的事实。

CORS

与浏览器不同,Undici 默认不实现 CORS 检查:

  • 不会为跨域请求自动发送 preflight;
  • 不校验 Access-Control-Allow-Origin 响应头;
  • 允许从任何来源向任何 origin 发起请求。

这是服务端场景的刻意设计(CORS 限制通常无必要)。如果应用需要类 CORS 保护,需自行实现。

垃圾回收与响应体消费

Fetch 标准允许用户跳过消费响应体、依赖垃圾回收释放连接资源。但 Node 的垃圾回收不如浏览器积极且确定性差(浏览器有渲染刷新率带来的明确空闲周期,Node 没有),把释放连接资源交给 GC 会导致:连接占用过多、连接复用率下降导致性能降低,甚至在连接耗尽时出现停滞或死锁。因此,务必总是消费或取消响应体:

// 推荐
const { body, headers } = await fetch(url);
for await (const chunk of body) {
  // 强制消费 body
}

// 不推荐
const { headers } = await fetch(url);

如果只想拿响应头,改用 HEAD 方法更合适,它无需消费或取消响应体:

const headers = await fetch(url, { method: 'HEAD' })
  .then(res => res.headers)

注意:request 必须消费响应体:

// 推荐
const { body, headers } = await request(url);
await body.dump(); // 强制消费 body

// 不推荐
const { headers } = await request(url);

Forbidden 与 Safelisted Header 名称

Fetch 标准要求实现排除某些请求/响应头——在浏览器中,部分头是 forbidden 的,以保证 user agent 对它们完全掌控。Undici 移除了这些约束,把控制权交给用户。这意味着服务端代码可以设置浏览器中被禁用的头(如 Host、Referer 等)。

Content-Encoding 层数限制

Undici 将响应中的 Content-Encoding 层数限制为 5,防止资源耗尽攻击。如果服务器返回超过 5 层编码(例如 Content-Encoding: gzip, gzip, gzip, gzip, gzip, gzip),fetch 会被以错误拒绝。该限制与 curl、urllib3 的做法一致。

Expect 请求头

Undici 不支持 Expect 请求头字段:请求体总是立即发送,100 Continue 响应会被忽略。

Pipelining

  • 只有将 pipelining 因子配置为大于 1 时,Undici 才会使用 pipelining;只在信任远端服务器时启用。同时必须给请求选项传 blocking: false 才能真正流水线化请求。
  • Undici 总是假设连接是持久的,会立即流水线化请求,不检查连接是否持久;因此不支持自动回退到 HTTP/1.0 或非 pipelining 的 HTTP/1.1。
  • 连接失败后重试时,Undici 会立即开始 pipelining;但不会重试先前流水线中剩余的首批请求,而是让对应的 callback / promise / stream 报错。
  • 当流水线中任意请求被 abort 时,Undici 会 abort 流水线中所有进行中的请求。

手动重定向(Manual Redirect)

由于服务端无法手动跟随 HTTP 重定向,Undici 在 manual redirect 模式下返回真实响应,而不是 opaqueredirect 过滤响应,这与 Deno 和 Cloudflare Workers 的实现一致。

工作区(Workarounds)

网络地址族自动选择(autoSelectFamily)

如果连接由 DNS 解析为 IPv6(AAAA 记录)优先的远端服务器时遇到问题(本地路由器或 ISP 可能无法连通 IPv6 网络),Undici 会抛出错误码 UND_ERR_CONNECT_TIMEOUT。若目标服务器同时解析出 IPv6 与 IPv4(A 记录),且 Node 版本兼容(18.3.0 及以上),可以通过 autoSelectFamily 选项修复(undici.request 与 undici.Agent 均支持),该选项会在建立连接时启用地址族自动选择算法。

长期支持(LTS)

Undici 与 Node.js 的 LTS 计划对齐。以下是 README 中的支持矩阵:

Undici Version Bundled in Node.js Node.js Versions Supported End of Life
5.x 18.x ≥14.0(测试于:14, 16, 18) 2024-04-30
6.x 20.x, 22.x ≥18.17(测试于:18, 20, 21, 22) 2027-04-30
7.x 24.x ≥20.18.1(测试于:20, 22, 24) 2028-04-30
8.x 26.x ≥22.19.0(测试于:22, 24, 26) 2029-04-30

当前仓库对应 8.x 主线(package.json 中版本为 8.11.2,engines.node 为 >=22.19.0)。选择独立安装 Undici 时,应根据自己的 Node.js 版本对照此表挑选合适的主版本。

进一步阅读

登录后查看全文
undici