一文搞懂 Fetch API

Fetch API 是现代浏览器提供的一个用于发起网络请求的 JavaScript 接口,它提供了一种更强大、更灵活的方式来处理 HTTP 请求,替代了传统的 XMLHttpRequest (XHR)。

Fetch API 提供了一个全局的 fetch() 方法,该方法返回一个 Promise 对象,用于获取网络资源。它的设计遵循"请求-响应"模型,使得处理异步网络请求更加简洁明了

核心组成部分:Request、Response、Headers

使用示例:

// GET
fetch("https://api.example.com/data")
  .then((response) => {
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    return response.json();
  })
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.error("There was a problem with the fetch operation:", error);
  });

// POST
fetch("https://api.example.com/data", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "John Doe",
    age: 30,
  }),
})
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => console.error("Error:", error));

高级特性:

  • 取消 Fetch 请求
  • 上传文件
  • 流式处理响应
  • 错误处理

Fetch 只在网络错误时 reject Promise,HTTP 错误状态 (如 404 或 500) 不会导致 reject。因此需要检查 response.okresponse.status

对比 XML

特性 Fetch API XMLHttpRequest
语法 基于 Promise,更简洁 回调方式,较复杂
流式处理 支持 不支持
请求取消 通过 AbortController 原生支持
响应类型 多种转换方法 需要手动设置
CORS 处理 更简单 较复杂
进度事件 不支持 支持
浏览器支持 现代浏览器 所有浏览器
请求头操作 Headers 对象 直接设置

问题:

1、它是什么时候出来的?

Fetch API 是在 2015 年正式成为 Web 标准的,具体时间线如下:

  • 2011 年:最初由 GitHub 团队提出概念
  • 2014 年:开始被主流浏览器实现
  • 2015 年:正式成为 WHATWG 标准的一部分
  • 2016 年:被纳入 W3C 标准

2、它为什么出来,有什么优势?

  • 基于 Promise 的简洁语法

  • 更合理的默认行为

    • 不会将 HTTP 错误状态(如 404/500)视为网络错误(XHR 会触发 onerror)
    • 自动处理 CORS 相关头部
    • 更简单的请求/响应抽象
  • 更现代的 API 设计

  • 流式数据处理能力

  • 更好的头信息管理

  • 更灵活的配置选项

3、它的缺陷

尽管 Fetch API 有很多优势,但也存在一些限制:

  1. 没有原生进度事件:上传/下载进度需要额外实现
  2. IE 完全不支持:必须使用 polyfill
  3. 默认不发送/接收 cookie:需要显式设置 credentials
  4. HTTP 错误不会 reject:需要手动检查 response.ok
  5. 较新的高级功能:如流式处理在某些浏览器支持不完整

4、日常开发中,我们应该如何选择?

在日常前端开发中,选择使用原生 Fetch API 还是 Axios 是一个常见的决策点。下面我将详细分析如何选择。

选择标准对比表:

标准 Fetch API Axios
浏览器兼容性 现代浏览器(需 polyfill 支持 IE) 全浏览器支持(包括 IE)
语法简洁性 较简洁 更简洁(特别是拦截器、取消等)
功能完整性 基础功能 全面功能(进度、取消、拦截器等)
错误处理 需手动检查 HTTP 错误 自动处理 HTTP 错误
请求/响应转换 需手动处理 自动转换 JSON 数据
超时控制 需结合 AbortController 实现 内置支持
CSRF 防护 需手动实现 内置支持
上传进度 不支持 支持
体积 原生 API(0kb) 约 4kb(压缩后)

具体选择建议

  1. 推荐使用 Fetch 的场景

    • 开发 PWA 或需要最小化依赖的项目
    • 只需要基础网络请求功能
    • 目标平台是现代浏览器(或已配置 polyfill)
    • 项目对包体积极度敏感
  2. 推荐使用 Axios 的场景

    • 需要支持旧版浏览器(如 IE11)
    • 项目需要请求/响应拦截器
    • 需要上传/下载进度跟踪
    • 需要更简洁的错误处理
    • 需要自动 JSON 转换
    • 项目已使用 Axios 且无迁移必要
  3. 现代替代方案考虑

    • 如果使用 React:可考虑 React Query 或 SWR
    • 如果使用 Vue:可考虑 VueRequest
    • 如果追求极致轻量:可考虑 redaxios(类似 Axios API 的 1kb 替代)

5、fetch 为什么需要两次 await 才能拿到数据?

Fetch API 设计中使用两次 await 的原因涉及响应处理流程的分阶段特性。

核心原因:分阶段响应处理

Fetch 将 HTTP 请求/响应过程明确分为两个阶段:

  1. 元数据获取阶段(第一个 await),理解为获取 Header 内容

    • 获取响应状态、头部等元数据
    • 验证响应是否成功(检查 response.ok
  2. 正文内容获取阶段(第二个 await),获取 Body 内容,此过程是一个异步状态

    • 实际读取响应体内容
    • 将原始数据转换为所需格式(JSON/text/blob 等)

详细流程解析

1. 第一次 await:获取 Response 对象

const response = await fetch(url);

这行代码完成后你得到的是:

  • HTTP 状态码(response.status
  • 响应头(response.headers
  • 响应是否成功的标志(response.ok
  • 一个未读取的响应体流response.body

此时响应体尚未被读取,因为:

  1. 性能优化:避免立即读取可能不需要的大响应体
  2. 灵活性:允许开发者先检查元数据再决定如何处理响应体

2. 第二次 await:获取响应体内容

const data = await response.json();

这个阶段实际完成:

  1. 从网络流中读取原始响应数据
  2. 将数据解析为指定格式(这里是 JSON)
  3. 返回解析后的结果

总结:

这样设计的目的:

  • 明确的流程控制:清晰分离元数据和内容处理
  • 性能优化:支持流式处理和延迟加载
  • 灵活性:允许开发者根据响应状态决定处理方式