Axios 或 fetch():您应该使用哪一个?
作者:法拉兹·凯尔希尼✏️
在我最近的文章“如何使用 Axios 像专业人士一样发出 HTTP 请求”中,我讨论了使用 Axios 库的优势。然而,必须承认 Axios 并非始终是理想的解决方案,有时还有更好的 HTTP 请求方案。
毫无疑问,一些开发者更喜欢 Axios,而不是内置 API,因为它易于使用。但许多人高估了对这样一个库的需求。该fetch()
API 完全能够重现 Axios 的关键功能,并且它还具有在所有现代浏览器中均可轻松访问的额外优势。
在本文中,我们将比较fetch()
Axios 和 JavaScript,并了解如何使用它们执行不同的任务。希望在阅读完本文后,您能够更好地理解这两个 API。
基本语法
在深入研究 Axios 的更多高级功能之前,让我们先将其基本语法与 进行比较fetch()
。以下是如何使用 AxiosPOST
向 URL 发送带有自定义标头的请求。Axios 会自动将数据转换为 JSON,因此您无需手动操作。
// axios
const options = {
url: 'http://localhost/test.htm',
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
},
data: {
a: 10,
b: 20
}
};
axios(options)
.then(response => {
console.log(response.status);
});
现在将此代码与fetch()
产生相同结果的版本进行比较:
// fetch()
const url = 'http://localhost/test.htm';
const options = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
},
body: JSON.stringify({
a: 10,
b: 20
})
};
fetch(url, options)
.then(response => {
console.log(response.status);
});
请注意:
- 要发送数据,
fetch()
使用 body 属性,而 Axios 使用 data 属性 - 中的数据
fetch()
是字符串化的 - URL 作为参数传递给
fetch()
。然而,在 Axios 中,URL 是在 options 对象中设置的。
向后兼容性
Axios 的主要卖点之一是其广泛的浏览器支持。即使是像 IE11 这样的老旧浏览器也能流畅运行 Axios。而 Axios 仅支持 Chrome 42+、Firefox 39+、Edge 14+ 和 Safari 10.1+(您可以在“我可以使用吗Fetch()
…… ”上查看完整的兼容性列表)。
如果您使用 Axios 的唯一原因是向后兼容,那么您实际上并不需要 HTTP 库。相反,您可以使用像这样fetch()
的 polyfill在不支持的 Web 浏览器上实现类似的功能。要开始使用 fetch polyfill,请通过 npm 命令安装它:fetch()
npm install whatwg-fetch --save
然后你可以发出这样的请求:
import 'whatwg-fetch'
window.fetch(...)
请记住,在某些旧浏览器中您可能还需要承诺 polyfill。
响应超时
Axios 中设置超时的简便性是一些开发人员更喜欢它的原因之一fetch()
。在 Axios 中,你可以使用 config 对象中的可选timeout
属性来设置请求中止前的毫秒数。例如:
axios({
method: 'post',
url: '/login',
timeout: 4000, // 4 seconds timeout
data: {
firstName: 'David',
lastName: 'Pollock'
}
})
.then(response => {/* handle the response */})
.catch(error => console.error('timeout exceeded'))
Fetch()
通过AbortController
界面提供类似的功能。但它不像 Axios 版本那么简单:
const controller = new AbortController();
const options = {
method: 'POST',
signal: controller.signal,
body: JSON.stringify({
firstName: 'David',
lastName: 'Pollock'
})
};
const promise = fetch('/login', options);
const timeoutId = setTimeout(() => controller.abort(), 4000);
promise
.then(response => {/* handle the response */})
.catch(error => console.error('timeout exceeded'));
AbortController
这里,我们使用构造函数创建一个对象AbortController.AbortController()
,以便稍后中止请求。signal
是一个只读属性,用于AbortController
提供与请求通信或中止请求的方法。如果服务器在四秒内没有响应,controller.abort()
则会调用 ,并终止操作。
自动 JSON 数据转换
正如我们之前所说,Axios 在发送请求时会自动对数据进行字符串化(尽管你可以覆盖默认行为并定义其他转换机制)。fetch()
然而,使用 时,你必须手动执行此操作。比较一下:
// axios
axios.get('https://api.github.com/orgs/axios')
.then(response => {
console.log(response.data);
}, error => {
console.log(error);
});
// fetch()
fetch('https://api.github.com/orgs/axios')
.then(response => response.json()) // one extra step
.then(data => {
console.log(data)
})
.catch(error => console.error(error));
数据自动转换是一项很好的功能,但同样,这并不是您无法做到的事情fetch()
。
HTTP 拦截器
Axios 的一大关键特性是能够拦截 HTTP 请求。当您需要检查或更改从应用程序到服务器(或反之亦然)的 HTTP 请求(例如日志记录、身份验证等)时,HTTP 拦截器会非常方便。使用拦截器,您无需为每个 HTTP 请求编写单独的代码。
以下是在 Axios 中声明请求拦截器的方法:
axios.interceptors.request.use(config => {
// log a message before any HTTP request is sent
console.log('Request was sent');
return config;
});
// sent a GET request
axios.get('https://api.github.com/users/sideshowbarker')
.then(response => {
console.log(response.data);
});
在这段代码中,该axios.interceptors.request.use()
方法用于定义在发送 HTTP 请求之前运行的代码。
默认情况下,fetch()
不提供拦截请求的方法,但想出一个解决方法并不难。您可以覆盖全局 fetch 方法并定义自己的拦截器,如下所示:
fetch = (originalFetch => {
return (...arguments) => {
const result = originalFetch.apply(this, arguments);
return result.then(console.log('Request was sent'));
};
})(fetch);
fetch('https://api.github.com/orgs/axios')
.then(response => response.json())
.then(data => {
console.log(data)
});
下载进度
进度指示器在加载大型资源时非常有用,尤其是对于网速较慢的用户。以前,JavaScript 程序员使用XMLHttpRequest.onprogress
回调处理程序来实现进度指示器。
Fetch API 没有处理程序。相反,它通过响应对象的 body 属性onprogress
提供一个实例。ReadableStream
下面的例子说明了如何使用ReadableStream
在图像下载过程中为用户提供即时反馈:
// original code: https://github.com/AnthumChris/fetch-progress-indicators
<div id="progress" src="">progress</div>
<img id="img">
<script>
'use strict'
const element = document.getElementById('progress');
fetch('https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg')
.then(response => {
if (!response.ok) {
throw Error(response.status+' '+response.statusText)
}
// ensure ReadableStream is supported
if (!response.body) {
throw Error('ReadableStream not yet supported in this browser.')
}
// store the size of the entity-body, in bytes
const contentLength = response.headers.get('content-length');
// ensure contentLength is available
if (!contentLength) {
throw Error('Content-Length response header unavailable');
}
// parse the integer into a base-10 number
const total = parseInt(contentLength, 10);
let loaded = 0;
return new Response(
// create and return a readable stream
new ReadableStream({
start(controller) {
const reader = response.body.getReader();
read();
function read() {
reader.read().then(({done, value}) => {
if (done) {
controller.close();
return;
}
loaded += value.byteLength;
progress({loaded, total})
controller.enqueue(value);
read();
}).catch(error => {
console.error(error);
controller.error(error)
})
}
}
})
);
})
.then(response =>
// construct a blob from the data
response.blob()
)
.then(data => {
// insert the downloaded image into the page
document.getElementById('img').src = URL.createObjectURL(data);
})
.catch(error => {
console.error(error);
})
function progress({loaded, total}) {
element.innerHTML = Math.round(loaded/total*100)+'%';
}
</script>
在 Axios 中实现进度指示器更简单,尤其是使用Axios Progress Bar模块时。首先,您需要包含以下样式和脚本:
<link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/nprogress.css" />
<script src="https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/index.js"></script>
然后你可以像这样实现进度条:
<img id="img">
<script>
loadProgressBar();
const url = 'https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg';
function downloadFile(url) {
axios.get(url, {responseType: 'blob'})
.then(response => {
const reader = new window.FileReader();
reader.readAsDataURL(response.data);
reader.onload = () => {
document.getElementById('img').setAttribute('src', reader.result);
}
})
.catch(error => {
console.log(error)
});
}
downloadFile(url);
</script>
此代码使用FileReader
API 异步读取已下载的图片。该readAsDataURL
方法将图片数据以 Base64 编码的字符串形式返回,然后将其插入到标签src
的属性中img
以显示图片。
同时请求
为了同时发出多个请求,Axios 提供了axios.all()
方法。只需将请求数组传递给此方法,然后使用axios.spread()
它将响应数组的属性赋值给单独的变量:
axios.all([
axios.get('https://api.github.com/users/iliakan'),
axios.get('https://api.github.com/users/taylorotwell')
])
.then(axios.spread((obj1, obj2) => {
// Both requests are now complete
console.log(obj1.data.login + ' has ' + obj1.data.public_repos + ' public repos on GitHub');
console.log(obj2.data.login + ' has ' + obj2.data.public_repos + ' public repos on GitHub');
}));
您可以使用内置Promise.all()
方法实现相同的结果。将所有获取请求作为数组传递给Promise.all()
。接下来,使用函数处理响应async
,如下所示:
Promise.all([
fetch('https://api.github.com/users/iliakan'),
fetch('https://api.github.com/users/taylorotwell')
])
.then(async([res1, res2]) => {
const a = await res1.json();
const b = await res2.json();
console.log(a.login + ' has ' + a.public_repos + ' public repos on GitHub');
console.log(b.login + ' has ' + b.public_repos + ' public repos on GitHub');
})
.catch(error => {
console.log(error);
});
结论
Axios 以紧凑的封装形式提供了易于使用的 API,可满足您的大多数 HTTP 通信需求。但是,如果您更喜欢使用原生 API,那么您可以轻松实现 Axios 的功能。
正如本文所讨论的,使用 Web 浏览器提供的方法完全可以重现 Axios 库的关键功能fetch()
。最终,是否值得加载客户端 HTTP API 取决于您是否习惯使用内置 API。
编者注:觉得这篇文章有什么问题?您可以在这里找到正确版本。
插件:LogRocket,一个用于 Web 应用的 DVR
LogRocket是一款前端日志工具,可让您重放问题,就像它们发生在您自己的浏览器中一样。您无需猜测错误发生的原因,也无需要求用户提供屏幕截图和日志转储,LogRocket 允许您重放会话以快速了解问题所在。它可与任何应用程序完美兼容,无论使用哪种框架,并且提供插件来记录来自 Redux、Vuex 和 @ngrx/store 的更多上下文。
除了记录 Redux 操作和状态之外,LogRocket 还记录控制台日志、JavaScript 错误、堆栈跟踪、带有标头 + 正文的网络请求/响应、浏览器元数据以及自定义日志。它还会对 DOM 进行插桩,以记录页面上的 HTML 和 CSS,即使是最复杂的单页应用程序,也能重现像素完美的视频。
免费试用。
文章Axios 或 fetch():您应该使用哪一个?最先出现在LogRocket 博客上。
文章来源:https://dev.to/bnevilleoneill/axios-or-fetch-which-should-you-use-95i