如何在javascript中发起网络请求_从Fetch API到Axios的最佳实践【教程】

应根据项目需求选择 Fetch 或 Axios:Fetch 轻量现代但需手动处理错误、超时和取消;Axios 提供拦截器、自动 JSON 解析等便利功能,但增加依赖。关键在统一错误分类与状态管理。

Fetch API 是现代浏览器发起网络请求的原生方案,但直接使用它容易忽略错误处理、超时控制和请求取消等关键问题;Axios 提供了更友好的默认行为和可扩展能力,但引入额外依赖需权衡。是否用 Axios,取决于你是否需要拦截器、自动 JSON 解析、取消请求或 IE 兼容性支持。

Fetch API 的常见陷阱与修复写法

原生 fetch() 不会因 HTTP 状态码(如 404、500)抛出错误,response.ok 必须手动检查;同时它不支持超时,且无法中止正在进行的请求(除非用 AbortController)。

  • 忘记检查 response.ok → 导致 4xx/5xx 响应被当作成功处理
  • 没传 signal → 请求卡死时无法主动中断
  • 未设置 Content-Typecredentials → 登录态丢失或后端拒收
const controller = new AbortController();
setTimeout(() => controller.abort(), 8000);

try {
  const res = await fetch('/api/data', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ id: 123 }),
    signal: controller.signal,
    credentials: 'include'
  });

  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  const data = await res.json();
} catch (err) {
  if (err.name === 'AbortError') console.log('请求已超时或取消');
  else console.error('请求失败:', err);
}

Axios 的配置要点与默认行为差异

Axios 默认自动解析 JSON 响应体,但默认不携带 cookies(withCredentials: false),且响应结构是包裹式的 response.data,不是裸数据。它的拦截器非常实用,但要注意:请求拦截器中修改 config.url 后,baseURL 不再生效。

  • axios.defaults.baseURL 只影响未以 http:// 开头的 URL
  • 响应拦截器里抛错,会进入 .catch(),但原始 response 已不可见 —— 需提前保存
  • 取消请求必须用 CancelToken.source()AbortController(v0.27+)
axios.defaults.withCredentials = true;

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

axios.interceptors.response.use(
  res => res.data, // 统一提取 data
  err => {
    if (err.response?.status === 401) logout();
    return Promise.reject(err);
  }
);

何时该坚持用 Fetch,而非引入 Axios

项目已用 ESM 按需加载、目标环境为现代浏览器(Chrome 62+/Firefox 57+)、团队倾向最小依赖、且不需要请求重试或复杂拦截逻辑时,Fetch 更轻量可控。尤其在 Service Worker 或 Deno 环境中,Axios 并不适用。

  • 构建产物大小敏感(Axios gzip 后约 4.5KB,Fetch 零成本)
  • 需要精细控制流:比如用 ReadableStream 处理大文件分块上传
  • 已有封装层(如 SWR、React Query)接管请求逻辑,Axios 的拦截器反而冗余

并发请求与错误聚合的实际处理方式

无论是 Promise.all() 还是 axios.all(),任一请求失败都会导致整个集合 reject。真实场景中往往需要“尽力而为”——即返回所有结果(含错误),而不是中断全部。

  • Promise.allSettled() 替代 Promise.all()

    ,获取每个请求的 { status: 'fulfilled' | 'rejected', value | reason }
  • Axios 中避免在单个 axios.all() 调用里混用不同 base URL 或超时策略
  • 对批量 ID 查询,优先考虑服务端聚合接口,而非前端发 N 个请求
const requests = ids.map(id =>
  fetch(`/api/user/${id}`).then(r => r.json()).catch(err => ({ error: err.message }))
);

const results = await Promise.allSettled(requests);
const successes = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const failures = results.filter(r => r.status === 'rejected').map(r => r.reason);

真正麻烦的不是选 Fetch 还是 Axios,而是统一错误分类(网络层?业务层?认证失效?)、标准化 loading 状态管理、以及让 timeout 和 abort 行为在 UI 上可感知 —— 这些几乎从不在库文档里写清楚,却直接影响调试效率。