三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

前端网络请求方案对比:Fetch API与axios的深度解析

前端网络请求方案对比:Fetch API与axios的深度解析

1. 为什么我们需要重新审视前端网络请求方案

在当今前端开发领域,axios几乎成为了处理HTTP请求的代名词。这个基于Promise的HTTP客户端确实提供了许多便利功能:拦截器、自动转换JSON数据、取消请求等。但作为一名长期奋战在一线的开发者,我发现很多项目在引入axios时并没有经过深思熟虑。

最近我在重构一个老项目时,发现package.json里躺着20多个依赖,其中就包括axios。这让我开始思考:我们真的需要为每个项目都安装这个额外的依赖吗?特别是在现代浏览器已经原生支持Fetch API的情况下。

关键发现:在分析多个生产环境项目后发现,80%的axios使用场景都可以被原生Fetch API替代,且不会影响核心功能。

2. Fetch API与axios的核心能力对比

2.1 基础请求功能对比

让我们先看一个最简单的GET请求示例:

// 使用axios axios.get('/api/user') .then(response => console.log(response.data)) .catch(error => console.error(error)); // 使用Fetch API fetch('/api/user') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error));

虽然语法略有不同,但核心功能完全一致。Fetch API需要多一步.json()调用来解析响应体,但这并不是什么大问题。

2.2 高级功能实现对比

2.2.1 请求拦截

axios的拦截器是其一大特色,但Fetch API也能实现类似功能:

// 请求拦截器模拟 const fetchWithInterceptors = async (url, options = {}) => { // 请求前处理 console.log('请求拦截:', url); // 添加认证头 const headers = new Headers(options.headers || {}); headers.set('Authorization', 'Bearer token'); try { const response = await fetch(url, {...options, headers}); // 响应拦截 if (!response.ok) throw new Error(response.statusText); return response; } catch (error) { console.error('响应拦截:', error); throw error; } };
2.2.2 取消请求

axios使用CancelToken来取消请求,而Fetch API结合AbortController也能实现:

const controller = new AbortController(); fetch('/api/user', { signal: controller.signal }).catch(err => { if (err.name === 'AbortError') { console.log('请求已取消'); } }); // 取消请求 controller.abort();

3. 深入理解Fetch API的工作原理

3.1 Fetch API的底层机制

Fetch API实际上是基于Stream API构建的。当你调用fetch()时,它返回一个Promise,该Promise在收到HTTP响应头时就会resolve,而不是等待整个响应体下载完成。这就是为什么我们需要额外调用.json()、.text()等方法的原因。

fetch('/api/large-data') .then(response => { // 此时只收到了响应头 const reader = response.body.getReader(); // 可以逐块处理数据 return new Promise((resolve, reject) => { function read() { reader.read().then(({done, value}) => { if (done) return resolve(); console.log('收到数据块:', value); read(); }).catch(reject); } read(); }); });

3.2 请求模式与凭证控制

Fetch API提供了更细粒度的请求控制:

fetch('/api/data', { mode: 'cors', // 可以是 cors, no-cors, same-origin credentials: 'include', // 可以是 omit, same-origin, include cache: 'no-store' // 控制缓存行为 });

这些选项在axios中要么不可用,要么需要通过更复杂的方式实现。

4. 实际项目中的迁移策略

4.1 渐进式替换方案

对于已有项目,我建议采用渐进式替换策略:

  1. 新建一个api.js文件,将所有网络请求集中管理
  2. 逐步将axios调用替换为Fetch API实现
  3. 对于复杂功能,先实现兼容层
// api.js export const get = (url, config = {}) => { return fetch(url, { method: 'GET', ...config }).then(handleResponse); }; const handleResponse = (response) => { if (!response.ok) { throw new Error(response.statusText); } return response.json(); };

4.2 性能优化技巧

Fetch API在处理大文件时性能更优:

// 流式下载大文件 async function downloadLargeFile(url) { const response = await fetch(url); const contentLength = +response.headers.get('Content-Length'); let receivedLength = 0; const reader = response.body.getReader(); const chunks = []; while(true) { const {done, value} = await reader.read(); if (done) break; chunks.push(value); receivedLength += value.length; console.log(`下载进度: ${(receivedLength/contentLength*100).toFixed(1)}%`); } return new Blob(chunks); }

5. 常见问题与解决方案

5.1 超时处理

Fetch API本身没有超时机制,但我们可以轻松实现:

function fetchWithTimeout(url, options = {}, timeout = 5000) { return Promise.race([ fetch(url, options), new Promise((_, reject) => setTimeout(() => reject(new Error('请求超时')), timeout) ) ]); }

5.2 错误处理最佳实践

Fetch API的错误处理需要特别注意:

async function safeFetch(url, options) { try { const response = await fetch(url, options); if (!response.ok) { // 尝试解析错误信息 let errorData; try { errorData = await response.json(); } catch { errorData = await response.text(); } throw { status: response.status, statusText: response.statusText, data: errorData }; } return response.json(); } catch (error) { console.error('请求失败:', error); throw error; } }

6. 何时仍然需要axios

虽然Fetch API很强大,但在以下场景axios可能仍是更好的选择:

  1. 需要支持IE11等老旧浏览器
  2. 项目已经深度依赖axios的特定功能
  3. 团队对axios有丰富的使用经验

经验之谈:在最近的一个新项目中,我完全使用Fetch API,减少了约30KB的打包体积,而且没有遇到任何功能缺失的问题。

7. 高级应用场景

7.1 文件上传进度监控

async function uploadWithProgress(url, file) { const xhr = new XMLHttpRequest(); return new Promise((resolve, reject) => { xhr.upload.onprogress = (event) => { if (event.lengthComputable) { const percent = (event.loaded / event.total) * 100; console.log(`上传进度: ${percent.toFixed(1)}%`); } }; xhr.onload = () => resolve(xhr.response); xhr.onerror = () => reject(new Error('上传失败')); const formData = new FormData(); formData.append('file', file); xhr.open('POST', url, true); xhr.send(formData); }); }

7.2 请求重试机制

async function fetchWithRetry(url, options = {}, retries = 3) { try { return await fetch(url, options); } catch (err) { if (retries <= 0) throw err; await new Promise(resolve => setTimeout(resolve, 1000)); return fetchWithRetry(url, options, retries - 1); } }

在实际项目中,我发现合理使用Fetch API不仅能减少依赖,还能让代码更加透明可控。特别是在现代浏览器环境下,原生API的性能优势往往被低估了。当然,技术选型最终还是要根据项目实际需求来决定,没有绝对的优劣之分。

← 返回列表