Back

相比 Fetch,Axios 仍然能为你提供什么

相比 Fetch,Axios 仍然能为你提供什么

Fetch API 已经相当成熟。它内置于每一款现代浏览器,自 v18 起通过 Undici 在 Node.js 中原生可用,能够处理 JSON、请求取消和流式传输。对于大多数前端 API 请求来说,它确实够用了。

那么,为什么团队仍然会选择 Axios?并非出于习惯,而是因为 Axios 仍然能解决一些 Fetch 留给你自己处理的开发体验问题。下面就是这些差距真正显现的地方。

核心要点

  • Fetch 在 4xx 和 5xx 响应上仍会成功 resolve,迫使你在每个请求中手动检查 response.ok
  • Axios 拦截器可以将认证令牌、日志记录、错误标准化等横切关注点集中处理,而无需为每个请求编写样板代码。
  • 通过 axios.create() 共享配置,简化了与多个需要不同 base URL、请求头或超时的 API 协同工作的过程。
  • 上传进度跟踪和内置超时选项仍是 Axios 独有的能力,Fetch 无法在原生层面与之匹敌。
  • 对于 HTTP 需求极少的小型项目,Fetch 是最合适的零依赖默认选项。

Axios vs Fetch:真正的差异在哪里

1. 默认即生效的 HTTP 错误处理

Fetch 仅在网络故障时才会 reject。404500 响应都会成功 resolve —— 你必须每次都自己检查 response.ok

// Fetch — 你必须手动检查
const res = await fetch('/api/user');
if (!res.ok) throw new Error(`HTTP error: ${res.status}`);
const data = await res.json();

// Axios — 默认在 4xx/5xx 时自动 reject
const { data } = await axios.get('/api/user');

在拥有数十个端点的大型应用中,这种手动检查会变成重复的样板代码。Axios 默认就帮你省去了大部分。


2. 内置的请求与响应拦截器

这是大多数开发者选择 Axios 时引用最多的特性。拦截器允许你一次性、全局地附加逻辑 —— 如认证令牌、日志记录、错误标准化 —— 而无需触碰单个请求。

axios.interceptors.request.use(config => {
  config.headers.Authorization = `Bearer ${getToken()}`;
  return config;
});

axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) redirectToLogin();
    return Promise.reject(error);
  }
);

使用 Fetch 时,你可以通过将 fetch() 包装在自定义函数中来模拟这种行为。但这种包装不可组合、不可堆叠,而且需要你自己维护。


3. 通过 Axios 实例共享配置

Axios 允许你创建带有预设 base URL、请求头和超时的隔离实例 —— 这种模式在你的应用需要与多个 API 通信时非常实用。

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 8000,
  headers: { 'X-App-Version': '2.1.0' },
});

要用 Fetch 干净地实现这一点,你需要一个包装类或工厂函数。可行,但这是你自己揽下的工作量。


4. 上传进度跟踪

Fetch 在浏览器中至今仍没有标准化的原生上传进度 API。Axios 直接暴露 onUploadProgress,在浏览器中底层由 XMLHttpRequest 支撑。

await axios.post('/upload', formData, {
  onUploadProgress: e => {
    if (e.total) {
      console.log(`${Math.round((e.loaded / e.total) * 100)}%`);
    }
  },
});

如果你在仅使用 Fetch 的环境中需要上传进度,就不得不退回到原始的 XMLHttpRequest。这是一种真正的倒退。


5. 无需 AbortController 样板代码的超时配置

Axios 直接接受 timeout 选项。Fetch 需要你手动接入 AbortControllersetTimeout —— 虽然可行,但增加了噪音。

// Axios
await axios.get('/api/data', { timeout: 5000 });

// Fetch 等效写法
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), 5000);
try {
  const res = await fetch('/api/data', { signal: controller.signal });
} finally {
  clearTimeout(id);
}

现代运行环境也支持 AbortSignal.timeout(),能大幅缩短这段代码,且浏览器支持目前已广泛覆盖。


速览对比:Axios 特性 vs Fetch API

能力FetchAxios
HTTP 错误拒绝手动检查 response.ok默认自动
拦截器需要自定义包装器内置
共享实例手动工厂模式axios.create()
上传进度原生不支持onUploadProgress
请求超时AbortController + setTimeouttimeout 选项
包体积0 KB(原生)约 15 KB(gzip 压缩后)

结论

Fetch 并不薄弱。对于简单的 JavaScript HTTP 客户端 —— 几个端点、没有共享认证逻辑、没有上传进度 —— 它就是合适的默认选择。无依赖、无额外开销。

但当你的前端 API 请求复杂度上升时,Axios 就配得上它的位置。拦截器、共享实例、自动错误处理和上传进度,这些特性在你不需要时不会想念它们 —— 一旦需要,你会希望它们是内置的,而不是手工实现的。这就是 Axios 仍然能为你提供的价值。

常见问题

这取决于项目的复杂度。对于少量直接的请求,Fetch 已经足够,并且可以避免引入依赖。但对于具有共享认证、集中式错误处理、多个 API 客户端或上传进度需求的应用,Axios 能省下大量你本来需要自己实现的样板代码。

部分可以。你可以将 fetch 包装在一个注入请求头或处理错误的函数中,但你会失去 Axios 拦截器的可堆叠性和可组合性。每个新的关注点都要直接修改包装器,串联多个独立的拦截器也变得别扭。大多数尝试这么做的团队最终会重建一个更小、测试更少的 Axios 版本。

并不完全相同。Axios 在 Node.js 中支持上传和下载进度,但实现方式与浏览器不同,因为 Node 不使用 XMLHttpRequest。浏览器中的进度事件通常更平滑、粒度更细,而某些 Node.js 上传场景 —— 尤其是 FormData 上传 —— 视适配器和运行时而定,可能存在一定限制。

两者都很扎实。Ky 是一个基于 Fetch 的小型包装器,提供重试、钩子和超时等功能,同时保持轻量。Got 功能丰富但仅适用于 Node。Axios 之所以仍然流行,是因为它能用同一套 API 同时在浏览器和 Node 环境中工作,拥有广泛的生态支持,并且为大多数 JavaScript 开发者所熟悉。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay