Axios或fetch()应该使用哪个?
现在相信有很多前端开发人员在用 Axios 库,它的好处用过的人都知道。但是,必须认识到 Axios 并非始终是理想的解决方案,并且有时会有更好的选择来发出 HTTP 请求。
毫无疑问,与内置 API 相比,某些前端开发人员更喜欢 Axios,因为它易于使用。但是许多人高估了对这样一个库的需求。该fetch()
API 完全能够重现 Axios 的关键功能,并且具有在所有现代浏览器中均可轻松获得的附加优势。
在本文中,我们将比较fetch()
和 Axios,看看如何将它们用于执行不同的任务。希望到本文结尾,大家将对这两个 API 有更好的了解。
基本语法
在深入研究 Axios 的更高级功能之前,我们先将其基本语法与fetch()
进行比较。这是使用 Axios 将 POST 带有自定义标头的请求发送到 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。另一方面,仅支持 Chrome 42+,Firefox 39+,Edge 14+和 Safari 10.1+
如果你使用 Axios 的唯一原因是向后兼容,那么你实际上就不需要 HTTP 库。取而代之的是,你可以使用这种类似的 polyfill 在不支持的网络浏览器上实现类似的功能。要开始使用fetch()
polyfill,请通过 npm 命令安装它:
npm install whatwg-fetch --save
然后,我们可以发出如下请求:
import 'whatwg-fetch' window.fetch(...)
注意,在某些旧的浏览器中,您可能还需要 promise 填充。
响应超时
另外,在 Axios 中设置 timeout 要比在fetch()
设置更加简单,这也是某些前端开发人员偏爱它的原因之一。在 Axios 中,你可以使用 config 对象中的可选 timeout 属性设置请求中止前的毫秒数。例如:
axios({ method: 'post', url: '/login', timeout: 4000, // 4 seconds timeout data: { firstName: 'mybj', lastName: 'qdbk' } }).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: 'mybj', lastName: 'qdbk' }) }; 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
是提供一种与请求进行通信或中止请求的方式的只读属性。如果服务器在不到四秒钟内没有响应,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 = (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) });
下载进度
加载大型资产时,进度指示器非常有用,尤其是对于互联网速度较慢的用户而言。以前,前端开发人员使用XMLHttpRequest.onprogress
实现进度指示器的回调处理程序。
Fetch()
API 没有onprogress
处理程序。相反,它通过响应对象的 body 属性提供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 进度条模块时。首先,您需要包含以下样式和脚本:
<link rel="stylesheet" type="text/css" rel="nofollow" href="https://mybj123.com/links?url=aHR0cHM6Ly9jZG4ucmF3Z2l0LmNvbS9yaWttbXMvcHJvZ3Jlc3MtYmFyLTQtYXhpb3MvMGEzYWNmOTIvZGlzdC9ucHJvZ3Jlc3MuY3Nz" /> <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>/
此代码使用 FileReaderAPI 异步读取下载的图像。该 readAsDataURL 方法以 Base64 编码的字符串形式返回图像的数据,然后将其插入 img 标签的 src 属性中以显示图像。
同时请求
为了同时发出多个请求,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 浏览器提供的fetch()
方法来重现 Axios 库的关键功能。最终,是否值得加载客户端 HTTP API 取决于您是否对使用内置 API 感到满意。
原文作者:Faraz Kelhini
原文链接:Axios or fetch(): Which should you use?
翻译:码云笔记
码云笔记 » Axios或fetch()应该使用哪个?