首页
/ Axios fetch 适配器实战:启用方式、进度捕获与自定义 fetch 环境(Tauri / SvelteKit)

Axios fetch 适配器实战:启用方式、进度捕获与自定义 fetch 环境(Tauri / SvelteKit)

2026-09-04 23:18:53作者:滑思眉Philip

Axios 从 1.7.0 版本起引入了 fetch 适配器,让你在保留 Axios 完整 API 的同时,用现代 fetch 底层完成请求。本文基于仓库中文档 fetch-adapter.md 与源码实现,系统讲解:如何显式启用 fetch 适配器、它在 xhr/http 之外的能力(进度捕获、stream/formdata 响应类型、URL 内嵌 Basic 认证),以及 v1.12.0 起通过 env 配置注入自定义 fetch/Request/Response 的完整方案(含 Tauri、SvelteKit 两个真实场景)。读完你能掌握 fetch 适配器的适配逻辑、能力边界与生产环境的接线方式。

一、fetch 适配器是什么,何时会被用到

fetch 适配器是 Axios 1.7.0 引入的新适配器,让你以 fetch API 作为传输层使用 Axios,"兼得两者之长"(Promise 化的 Axios 配置体系 + 现代 fetch 运行时)。

默认的适配器选择顺序

Axios 的默认 adapter 配置是一个按优先级尝试的列表,在 lib/defaults/index.js 中定义:

adapter: ['xhr', 'http', 'fetch'],

解析逻辑位于 lib/adapters/adapters.jsgetAdapter:按顺序遍历列表,对每个名字去 knownAdapters 表中查找,取第一个"可用"的适配器;全部失败时抛出 AxiosError.ERR_NOT_SUPPORT,错误信息会区分"该适配器不在 build 中"(is not available in the build)与"环境不支持"(is not supported by the environment)。其中 fetch 在表中是一个带 getter 的特殊条目(adapters.js L16-L22):

const knownAdapters = {
  http: httpAdapter,
  xhr: xhrAdapter,
  fetch: {
    get: fetchAdapter.getFetch, // 惰性解析,见下文
  },
};

也就是说:只有当 xhrhttp 都不在你的 build 中、或当前环境不支持时,fetch 才会作为兜底被自动选中。想强制默认走 fetch,必须显式把 adapter 设为 'fetch'

import axios from 'axios';

const instance = axios.create({
  adapter: 'fetch',
});

能力对齐:与 xhr 适配器相同的功能集

文档明确 fetch 适配器支持与 xhr 适配器相同的功能,特别是上传/下载进度捕获,并额外支持 streamformdata 等响应类型(取决于环境)。这一点在源码 lib/adapters/fetch.js 中有完整对应:

  • 响应类型解析器fetch.js L145-L167):注册了 textarrayBufferblobformDatastream 五种 resolver。其中 stream 仅在环境支持 Response.bodyReadableStream 时可用(supportsResponseStream 检测,L140-L143),否则会回落到抛出 Response type 'stream' is not supportedAxiosError.ERR_NOT_SUPPORT
  • 上传进度fetch.js L351-L413):当环境同时具备 Request 构造器、ReadableStream 且通过 duplex 特性探测(supportsRequestStream)时,请求体会被包成可追踪的流,逐块(默认块大小 DEFAULT_CHUNK_SIZE = 64 * 1024L18)回调 onUploadProgress
  • 下载进度fetch.js L508-L555):当需要 onDownloadProgress(或需要强制 maxContentLength)时,response.body 流被 trackStream 包裹,按块累计字节数后回调进度事件。

这也解释了后文一个重要限制:进度捕获依赖 Request/Response 构造器。若你在 env 中把它们设为 nullsupportsRequestStreamsupportsResponseStream 都会为 false,上传/下载进度就无法捕获。

此外源码中还包含一批值得了解的默认行为:

行为 源码位置 说明
默认 fetch 选项 fetch.js L20-L30 cache: 'default'redirect: 'follow'mode: 'cors' 等,仅当调用方未指定时生效
取消与超时信号合成 fetch.js L232-L235 composeSignals([signal, cancelToken.toAbortSignal()], timeout),三者统一为一个 AbortSignal
maxRedirects: 0 fetch.js L478-L484 对应 redirect: 'manual',不再自动跟随重定向
withCredentials fetch.js L217 默认 'same-origin',布尔值会映射为 'include'/'omit';Cloudflare Workers 等不支持 credentials 的环境会自动跳过(L421
User-Agent fetch.js L437 未设置时写入 axios/<版本>,避免 Node 下 fetch 默认的 node 标识
请求头安全清洗 fetch.js L456 通过 toByteStringHeaderObject 规范化,剥离 CRLF 注入字符

二、从 URL 读取 HTTP Basic 认证凭据

一个容易忽略的能力:auth 配置被省略时,fetch 适配器可以直接从请求 URL 中读取 Basic 认证凭据,例如 https://user:pass@example.com。URL 中百分号编码的凭据(如 my%40email.com)会先解码,再用于生成 Authorization 头;而显式配置的 auth 始终优先于 URL 内嵌凭据。

源码对应 lib/adapters/fetch.js L276-L301 的三段逻辑:

  1. maybeWithAuthCredentials(url) 预检 URL 在协议之后是否包含 @:L71-L78);
  2. 命中后解析 URL,若未配置 auth,则取 parsedURL.username/password 并经 decodeURIComponentSafe 解码(非法编码时回退原值、不抛错,L51-L61);随后会把 URL 中的用户名/密码清空,避免凭据随 URL 泄漏到日志与重定向链;
  3. 最终删除旧 authorization 头并写入 Authorization: Basic <btoa(user:pass)>。编码使用 encodeUTF8 把 UTF-8 转为 Latin-1 字节串再 btoaL42-L45),这是对已废弃的 unescape(encodeURIComponent(str)) 写法的现代替代,保证含非 ASCII 字符的密码也能正确编码。

三、自定义 fetch(v1.12.0+)

从 v1.12.0 开始,你可以通过 env 配置项向 fetch 适配器注入自定义的 fetch 函数以及配套的 RequestResponse 构造器,替代环境全局实现。这对运行在自定义运行时或自带 fetch 实现的应用框架(Tauri、SvelteKit、Cloudflare Workers 等)中的 Axios 非常关键。

env 的类型定义见 index.d.ts L443-L451

env?: {
  FormData?: new (...args: any[]) => object;
  fetch?: (input: URL | Request | string, init?: RequestInit) => Promise<Response>;
  Request?: new (input: URL | Request | string, init?: RequestInit) => Request;
  Response?: new (
    body?: ArrayBuffer | ArrayBufferView | Blob | FormData | URLSearchParams | string | null,
    init?: ResponseInit
  ) => Response;
};

注意两点实现细节:

  • 默认值合并:适配器工厂 factory(env) 会把传入的 env 与全局 globalThis.Request/ResponseskipUndefined 合并(fetch.js L80-L99)。因此省略 Request/Response 时会用全局构造器;而你的自定义 fetch 若与全局构造器不兼容,应显式传 null 关闭它们。
  • 按环境组合缓存getFetch(config)RequestResponsefetch 三元组为键在 seedCacheMap)中缓存适配器工厂结果(fetch.js L669-L692),同一套环境组合只构建一次;这也正是 knownAdapters.fetch 使用 getter 惰性解析的原因。
  • 进度捕获的代价Request/Response 设为 null 后,上传/下载进度捕获不可用(与第一节的能力矩阵一致)。

基本示例

import customFetchFunction from 'customFetchModule';

const instance = axios.create({
  adapter: 'fetch',
  onDownloadProgress(e) {
    console.log('downloadProgress', e);
  },
  env: {
    fetch: customFetchFunction,
    Request: null, // null -> 禁用该构造器
    Response: null,
  },
});

与 Tauri 配合使用

Tauri 提供了平台级 fetch 函数,来自原生层发起的请求可以绕过浏览器的 CORS 限制。下面是 Tauri 应用中接线的最小配置:

import { fetch } from '@tauri-apps/plugin-http';
import axios from 'axios';

const instance = axios.create({
  adapter: 'fetch',
  onDownloadProgress(e) {
    console.log('downloadProgress', e);
  },
  env: {
    fetch,
  },
});

const { data } = await instance.get('https://google.com');

这里只注入 fetch 而未禁用 Request/Response,因此仍可捕获下载进度。

与 SvelteKit 配合使用

SvelteKit 为服务端 load 函数提供了一个自定义 fetch 实现,负责 Cookie 透传与相对 URL 处理。由于它的 fetch 与标准 URL API 不兼容,必须显式让 Axios 使用它,并且同时禁用全局 RequestResponse 构造器

export async function load({ fetch }) {
  const { data: post } = await axios.get('https://jsonplaceholder.typicode.com/posts/1', {
    adapter: 'fetch',
    env: {
      fetch,
      Request: null,
      Response: null,
    },
  });

  return { post };
}

这也是"传 null 禁用构造器"用法的直接推论:代价是失去进度事件,换来与宿主运行时 100% 兼容。

四、请求体与响应体的大小守卫

fetch 适配器对 maxContentLength / maxBodyLength 的执行比"简单比较"细致得多,理解这些有助于排查超限错误:

  • maxContentLength:先做廉价的预检查——若服务端 Content-Length 已超上限,直接拒绝(fetch.js L496-L506);对流式响应则在 trackStream 回调中逐块累计、超限即抛 ERR_BAD_RESPONSE;在不支持 ReadableStream 的旧运行时,回退为对物化结果按 byteLength/size/字符串字节数校验(L567-L589)。对 data: URL 还会先估算解码后字节数,避免物化超大载荷(L306-L316)。
  • maxBodyLength:发送前对可确定尺寸的请求体取实际尺寸校验,刻意不信任调用方声明的 Content-Length(防止低报绕过,L318-L331);流式请求体则在 fetch 消费时逐块计数(L338-L349)。

错误归因上,fetch 适配器还处理了若干运行时差异:Safari 的 abort 可能抛出 getter 会抛异常的 DOMException 类对象,源码优先读取合成 signal 的 reason 以保留"超时 vs 取消"语义(L609-L625);TypeError 且消息匹配 /Load failed|fetch/ 时映射为 AxiosError.ERR_NETWORK("Network Error"),并把原始错误挂到不可枚举的 cause 上(L644-L662)。

五、测试验证与延伸阅读

仓库中 tests/unit/adapters/fetch.test.js 是 fetch 适配器的主测试套件(1800+ 行,基于本地 HTTP 服务),覆盖:

  • 非法 URL 在 fetch 归一化前被拒绝并保留 configERR_INVALID_URL);
  • 请求头 CRLF 字符清洗(防止头注入);
  • 不继承 Symbol.iterator 污染的头构造;
  • 以及进度、认证、流式响应、信号等核心路径。

如果你还想对比三种适配器的分工,可参考文档 adapters.md;fetch 适配器的原始文档见 docs/pages/advanced/fetch-adapter.md

六、速查清单

场景 配置要点
强制使用 fetch 底层 axios.create({ adapter: 'fetch' })
依赖自动兜底 无需配置;xhr/http 不可用时自动落到 fetch
注入框架自带 fetch(如 Tauri) env: { fetch },保留全局构造器以保留进度能力
注入不兼容全局 URL API 的 fetch(如 SvelteKit load) env: { fetch, Request: null, Response: null },放弃进度事件
URL 内嵌 Basic 凭据 直接写 https://user:pass@example.comauth 配置优先级更高
精确控制重定向 maxRedirects: 0 等价于 redirect: 'manual'

适用前提与限制:以上结论均基于当前仓库源码(lib/adapters/fetch.jslib/adapters/adapters.jslib/defaults/index.js),对应能力要求环境提供全局 fetchstream/formdata 响应类型与进度捕获分别依赖 ReadableStreamRequest/Response 构造器的可用性,注入自定义环境时请按第三节的能力矩阵自行权衡。

登录后查看全文
热门项目推荐
相关项目推荐