一篇 JS 的 Fetch API 与 axios 库的详细对比

本文是我个人在学习 AJAX (也就是 JS 异步编程)时候的学习笔记,假设你已掌握 axios 和
XMLHttpRequest
的情况下撰写。
关于三种请求方式
IE 时代 JS 里发起请求的方式是使用 XMLHttpRequest
对象,可见 使用 XMLHttpRequest 对象。这个方式没有实现异步处理,代码阅读感受比较反直觉。
Axios 是一个成熟的库,封装了 XMLHttpRequest
和 Promise
,并且提供了更多好用的特性和工具,设计哲学是“为开发者多做一点”。
Fetch API
是现代浏览器原生支持的用于替代 XMLHttpRequest
的东西,也是基于 promise 实现的。
我该用哪种
首先,XMLHttpRequest
在现代前端开发中几乎不用了。至于后面的两者,简单总结:
- 追求专业、原生、细粒度的控制:使用
Fetch api
- 使用更多现成的特性,大型、工程化项目,或者要兼容 IE 的项目:使用
axios
个人总结的一些区别
设计哲学
axios 设计哲学是为开发者多做点,其提供了很多方便的特性和工具,fetch 则相对低阶,没有这些特性。
请求参数
比如当你想实现这样的请求 URL:
https://example.com/api?username=leaves&password=123
你完全可以设置 axios 的 params 参数实现这一点,但是在 fetch 里你可能就只能自己构建 URLSearchParams
对象来实现:
1 | const pname = '浙江省' |
之后再使用模板字符串拼接。不过如果用的多的话,你当然也可以自己封装函数模拟 axios 的这些工具。
请求拦截器
axios 自带的一个很好用的特性,可以用来给每一个请求添加 token。
类似的,axios 还可以设置 baseURL。
请求类型
无论是用 axios 还是 XMLHttpRequest
,devtools 里看请求类型都是 XHR ,而 fetch api 则会显示 fetch :
这就说明了 fetch api 是一个完全的新东西。
请求头
axios 会自动设置请求头,而 fetch 需要显示强制设置请求头,尤其是 Content-Type
。
对比下面这两段代码,他们是等价的:
1 | fetch('http://geek.itheima.net/v1_0/authorizations',{ |
1 | axios({ |
我们可以看到 fetch 部分的代码里请求头有设置
1 | headers: { |
其中:
Content-Type
是必须的,否则某些后端会拒绝接受请求。Accept
不设置,fetch 会默认设置成Accept: */*
,而 axios 会默认设置成Accept: application/json, text/plain, */*
,表示“更期望对方返回application/json
类型”。
请求体
axios
请求体用data
字段设置,可以直接传入JS 对象。fetch
请求体用body
字段设置,必须是字符串、Blob等,不能是对象。
刚刚请求头那边的示例代码中,我们就将一个对象转换为 JSON 字符串给 body 使用:
1 | body: JSON.stringify(data) |
响应处理
- axios 一步到位,直接使用
response.data
即可。response
是.then()
方法里的形参。 - fetch 需要两步:
- 检查
response.ok
(这点下文[[#HTTP 状态错误]]会详细说明) 调用 response.json()
- 检查
使用 fetch 和 axios 都会返回一个 promise ,都可以用 .then()
方法接受,但是其中接受到的形参 response
并不是一个东西。
还是拿刚才的代码举例:
1 | // data 是一个对象,里面存着表单数据 |
两者都打印了 response
,我们来看看里面到底是啥:
图中我们可以看到,fetch api
的 body 里的内容是 ReadableStream
流数据,我们看不到里面具体的内容的(实在想看可以去在 Devtools 的 Network 面板)。而相比之下,axios 的 data
部分已经把数据整理好,能直接使用。
一个巧妙的比喻
- 想象使用 fetch 是网购了东西收快递,response 就是收到的快递盒,上面可以看到发件人、发件状态等诸多描述,但是就是不能直接用里面的东西;
- axios 相比之下就是你的私人管家,他帮你代收了件并且拆开,整理好,端盘到你面前给你用。
那到底怎么用 fetch 的 response 里的数据呢?
要看到 response 里面是啥,我们需要再写一个异步代码来解析流数据,而怎么解析可以由我们自己决定,这点充分体现了 fetch
方式的细粒度控制。
为什么设计成非要用异步代码来解析?
这是因为解析数据的过程很耗时,尤其是数据量大的时候。我们总不能卡在主线程等它慢慢解析吧。
通常情况下,我们会解析成 json :
1 | const data = await response.json() |
不过尽管如此,还是有 Response.blob() 方法,用于类文件数据 以及 Response:formData() 方法 等。
错误处理
axios 的错误处理做的很贴心(下文会提及),但如果对 fetch api 的理解够深刻,我们同样能完成优雅、细致的错误处理。
我们知道 fetch 和 axios 都会返回一个 promise 对象,既然如此那就都有 reject
和 resolve
。最关键的一个区别:
- 不管是什么类型的错误,axios 都会返回
reject
,被其后的 catch 语句捕获。 - 仅仅当请求无法发出时(比如断网、DNS 解析失败、CORS 策略阻拦),fetch 的 Promise 才会
reject
。而其他HTTP 状态错误或者数据解析错误等,fetch 依然会返回resolve
,我们需要额外的处理。
Fetch API 中
书接上文,fetch 的 reject 对应着网络层的错误,那么对于其他的错误,我们可以这样处理:
HTTP 状态错误
fetch 的 response 对象有一个布尔值类型的 ok 属性很实用。
response.ok
等价于 response.status >= 200 && response.status < 300
,来判断服务器是否成功处理了请求。
通常的实践是,先检查 response.ok
再写一个异步函数处理 response 里的 body 数据流,因为如果不 ok,那你解析了也没集贸用。
数据解析错误
既然需要写一个异步函数处理 response 里的 body 数据流,那这个过程中就有出错的可能(比如服务器返回的响应数据格式有问题,导致JSON 解析错误,response.json() 失败)。
对于这种错误,如果是在 promise 链式调用里处理,那会被最后一个 catch 捕获。如果使用了 async/await 语法糖,那就用 try...catch..
处理即可。
一些示例代码
我这里写了一个混合了 promise 和 await 的示例,清晰展示了处理三种错误的逻辑:
1 | fetch('http://geek.itheima.net/v1_0/authorizations', { |
下面是一个更详细的仅使用 async/await 的错误处理逻辑。在该逻辑下,fetch 实现了几乎等同于 axios 那么详细的错误处理分层,缺点在于要写一大堆 if 判断语句,这是因为 .then 链式调用过程中的任何错误都会并入异常捕获网结尾的那个 catch。不过在开发实践中,确实更倾向于在一处统一处理所有错误:
1 | form.addEventListener('submit', async function (e) { |
如果不使用 async/await ,标准链式调用的 promise 的写法可能如下:
1 | fetch('https://api.example.com/data') |
axios 中
axios 在错误处理上做了一层非常贴心的标准化封装。无论底层的错误是来自 XMLHttpRequest (浏览器)、http 模块 (Node.js),还是 axios 自己的逻辑(比如超时、取消请求),它都会将错误统一包装成一个特定的 Error 对象,然后 reject 出来(之后可以被 catch 语句接受)。
当 axios 请求失败时,.catch(error) 捕获到的 error 对象通常包含以下关键信息:
-
error.isAxiosError: 一个布尔值,true,可以用来方便地判断这是不是一个 axios 抛出的标准错误。
-
error.message: 错误的描述信息,比如 Network Error,或者 Request failed with status code 404。
-
error.config: 发起请求时的配置对象,包含了 url, method, headers, data 等所有信息,非常便于调试。
-
error.code: 错误码,比如 ECONNABORTED (超时)。
-
error.response: 这是最重要的部分! 如果错误是服务器返回的(即 HTTP 状态码不是 2xx),error.response 就会存在。它包含:
-
error.response.data
: 服务器返回的响应体(比如 { “message”: “用户名或密码错误” })。这是我们最常用来给用户展示错误信息的地方。 -
error.response.status
: HTTP 状态码,如 401, 404, 500。 -
error.response.headers
: 服务器返回的响应头。
-
-
error.request: 如果请求已发出但没有收到响应(比如网络错误),这个属性会存在。它通常是底层的 XMLHttpRequest 实例或 Node.js 的 ClientRequest 实例。
axios 错误处理示例
1 | axios.get('https://api.example.com/non-existent-page') |
- 标题: 一篇 JS 的 Fetch API 与 axios 库的详细对比
- 作者: 三葉Leaves
- 创建于 : 2025-06-18 00:00:00
- 更新于 : 2025-07-10 13:40:50
- 链接: https://blog.oksanye.com/6a6fd611eb6b/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。