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.ok
或response.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 有很多优势,但也存在一些限制:
- 没有原生进度事件:上传/下载进度需要额外实现
- IE 完全不支持:必须使用 polyfill
- 默认不发送/接收 cookie:需要显式设置 credentials
- HTTP 错误不会 reject:需要手动检查 response.ok
- 较新的高级功能:如流式处理在某些浏览器支持不完整
4、日常开发中,我们应该如何选择?
在日常前端开发中,选择使用原生 Fetch API 还是 Axios 是一个常见的决策点。下面我将详细分析如何选择。
选择标准对比表:
标准 | Fetch API | Axios |
---|---|---|
浏览器兼容性 | 现代浏览器(需 polyfill 支持 IE) | 全浏览器支持(包括 IE) |
语法简洁性 | 较简洁 | 更简洁(特别是拦截器、取消等) |
功能完整性 | 基础功能 | 全面功能(进度、取消、拦截器等) |
错误处理 | 需手动检查 HTTP 错误 | 自动处理 HTTP 错误 |
请求/响应转换 | 需手动处理 | 自动转换 JSON 数据 |
超时控制 | 需结合 AbortController 实现 | 内置支持 |
CSRF 防护 | 需手动实现 | 内置支持 |
上传进度 | 不支持 | 支持 |
体积 | 原生 API(0kb) | 约 4kb(压缩后) |
具体选择建议
推荐使用 Fetch 的场景:
- 开发 PWA 或需要最小化依赖的项目
- 只需要基础网络请求功能
- 目标平台是现代浏览器(或已配置 polyfill)
- 项目对包体积极度敏感
推荐使用 Axios 的场景:
- 需要支持旧版浏览器(如 IE11)
- 项目需要请求/响应拦截器
- 需要上传/下载进度跟踪
- 需要更简洁的错误处理
- 需要自动 JSON 转换
- 项目已使用 Axios 且无迁移必要
现代替代方案考虑:
- 如果使用 React:可考虑 React Query 或 SWR
- 如果使用 Vue:可考虑 VueRequest
- 如果追求极致轻量:可考虑 redaxios(类似 Axios API 的 1kb 替代)
5、fetch 为什么需要两次 await 才能拿到数据?
Fetch API 设计中使用两次 await 的原因涉及响应处理流程的分阶段特性。
核心原因:分阶段响应处理
Fetch 将 HTTP 请求/响应过程明确分为两个阶段:
元数据获取阶段(第一个 await),理解为获取 Header 内容
- 获取响应状态、头部等元数据
- 验证响应是否成功(检查
response.ok
)
正文内容获取阶段(第二个 await),获取 Body 内容,此过程是一个异步状态
- 实际读取响应体内容
- 将原始数据转换为所需格式(JSON/text/blob 等)
详细流程解析
1. 第一次 await:获取 Response 对象
const response = await fetch(url);
这行代码完成后你得到的是:
- HTTP 状态码(
response.status
) - 响应头(
response.headers
) - 响应是否成功的标志(
response.ok
) - 一个未读取的响应体流(
response.body
)
此时响应体尚未被读取,因为:
- 性能优化:避免立即读取可能不需要的大响应体
- 灵活性:允许开发者先检查元数据再决定如何处理响应体
2. 第二次 await:获取响应体内容
const data = await response.json();
这个阶段实际完成:
- 从网络流中读取原始响应数据
- 将数据解析为指定格式(这里是 JSON)
- 返回解析后的结果
总结:
这样设计的目的:
- 明确的流程控制:清晰分离元数据和内容处理
- 性能优化:支持流式处理和延迟加载
- 灵活性:允许开发者根据响应状态决定处理方式