Fetch API 替代 axios 的实践指南与功能对比
1. 为什么考虑替代 axios?
在前端开发中,axios 长期以来都是处理 HTTP 请求的首选库。但现代浏览器原生提供的 Fetch API 已经足够强大,能够满足大多数场景的需求。使用原生 API 最直接的好处就是减少项目依赖,降低打包体积。根据实测,在典型项目中移除 axios 可以减少 10-15KB 的打包体积(gzip 后)。
原生 Fetch API 从 2015 年就开始被主流浏览器支持,目前所有现代浏览器都已实现。特别是在需要支持现代浏览器的项目中,完全没有必要额外引入 HTTP 请求库。我最近在一个新项目中完全使用 Fetch API 替代 axios,发现开发体验几乎没有差别,反而因为减少了抽象层,调试起来更加直观。
2. Fetch API 基础用法解析
2.1 发起 GET 请求
最基本的 GET 请求非常简单:
fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error));与 axios 不同的是,Fetch API 返回的 Promise 不会直接 reject HTTP 错误状态(如 404 或 500)。需要手动检查 response.ok 属性:
fetch('https://api.example.com/data') .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) // ...2.2 发起 POST 请求
POST 请求需要额外配置 options 参数:
fetch('https://api.example.com/data', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ key: 'value' }) }) .then(response => response.json()) // ...3. 高级功能实现
3.1 请求超时处理
Fetch API 本身没有内置超时功能,但可以通过 Promise.race 实现:
const timeout = (ms) => new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), ms) ); Promise.race([ fetch('https://api.example.com/data'), timeout(5000) ]) .then(response => response.json()) // ...3.2 取消请求
使用 AbortController 可以取消正在进行的请求:
const controller = new AbortController(); const signal = controller.signal; fetch('https://api.example.com/data', { signal }) .then(response => response.json()) // ... // 取消请求 controller.abort();3.3 上传进度监控
对于大文件上传,可以通过读取 ReadableStream 来监控进度:
const response = await fetch('https://api.example.com/upload', { method: 'POST', body: largeFile, }); const reader = response.body.getReader(); let receivedLength = 0; while(true) { const {done, value} = await reader.read(); if (done) break; receivedLength += value.length; console.log(`Received ${receivedLength} bytes`); }4. 与 axios 的功能对比
4.1 功能差异对照表
| 功能 | Fetch API | axios |
|---|---|---|
| 浏览器支持 | 现代浏览器 | 所有浏览器 |
| 请求取消 | 需要 AbortController | 内置支持 |
| 超时处理 | 需要手动实现 | 内置支持 |
| 自动转换 JSON | 需要手动调用 .json() | 自动转换 |
| 拦截器 | 无 | 有 |
| 上传/下载进度 | 需要手动实现 | 内置支持 |
| CSRF 防护 | 需要手动设置 | 内置支持 |
| 请求/响应转换 | 无 | 有 |
4.2 何时应该坚持使用 axios
虽然 Fetch API 很强大,但在以下场景可能还是需要 axios:
- 需要支持非常旧的浏览器(IE11 及以下)
- 项目已经重度依赖 axios 的拦截器功能
- 需要开箱即用的上传/下载进度监控
- 团队对 axios 有很强的熟悉度,切换成本过高
5. 常见问题与解决方案
5.1 处理 CORS 问题
Fetch API 默认的 credentials 策略与 axios 不同。如果需要发送 cookies,需要显式设置:
fetch('https://api.example.com/data', { credentials: 'include' })5.2 错误处理最佳实践
建议封装一个统一的错误处理函数:
async function fetchWithErrorHandling(url, options) { try { const response = await fetch(url, options); if (!response.ok) { const error = new Error(response.statusText); error.response = response; throw error; } return await response.json(); } catch (error) { // 统一错误处理逻辑 console.error('Fetch error:', error); throw error; } }5.3 性能优化技巧
- 复用 Headers 对象:
const headers = new Headers(); headers.append('Content-Type', 'application/json'); // 后续请求可以复用这个 headers 对象- 使用 keepalive 标志提升页面卸载时的请求可靠性:
fetch('/analytics', { method: 'POST', body: analyticsData, keepalive: true });6. 实际项目迁移经验
我在最近的项目中从 axios 迁移到 Fetch API,总结了一些实用经验:
- 渐进式迁移:可以先在新功能中使用 Fetch API,逐步替换旧代码
- 封装工具函数:根据项目需求封装几个常用的 fetch 工具函数
- 类型安全:如果是 TypeScript 项目,为 fetch 响应添加类型注解
- 测试覆盖:确保所有边界情况都有测试覆盖,特别是错误处理
一个实用的 TypeScript 封装示例:
interface FetchOptions extends RequestInit { timeout?: number; } async function enhancedFetch<T = any>( url: string, options: FetchOptions = {} ): Promise<T> { const { timeout = 8000, ...fetchOptions } = options; const controller = new AbortController(); const { signal } = controller; const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { ...fetchOptions, signal }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } finally { clearTimeout(timeoutId); } }7. 生态系统补充
虽然 Fetch API 是浏览器原生功能,但有些场景下可能需要一些辅助工具:
- fetch-retry:为 fetch 添加自动重试功能
- cross-fetch:提供一致的 Node.js 和浏览器 fetch 实现
- redaxios:模仿 axios API 的轻量级 fetch 封装
对于大多数项目来说,这些工具都不是必需的,原生 Fetch API 已经足够强大。只有在特定需求下才需要考虑引入这些辅助库。