2019 年 9 月 18 日· 5 分钟阅读
在我最近的文章“如何像专业人士一样使用 Axios 发出 HTTP 请求”中,我讨论了使用 Axios 库的好处。尽管如此,重要的是承认 Axios 并不总是一个理想的解决方案,有时有发出 HTTP 请求的更好选择。
毫无疑问,一些开发人员更喜欢 Axios 而不是内置 API,因为它易于使用采用。但许多人高估了对这样一个库的需求。的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 property。fetch() 中的数据是字符串化的URL 作为参数传递给fetch()。然而,在 Axios 中,URL 是在option对象中设置。
向后兼容
Axios 的主要卖点之一是其广泛的浏览器支持。甚至旧IE11 等浏览器可以毫无问题地运行 Axios。另一方面,fetch(),仅支持 Chrome 42+、Firefox 39+、Edge 14+ 和 Safari 10.1+。
如果您使用 Axios 的唯一原因是向后兼容,那么您实际上并没有需要一个 HTTP 库。相反,你可以使用fetch()polyfill 来在不支持fetch() 的网络浏览器上实现类似的功能。
要开始使用 fetch polyfill,请通过 npm 命令安装它。
npm install whatwg-fetch --save
然后你可以提出这样的请求:
import 'whatwg-fetch'
window.fetch(...)
请记住,您可能还需要在一些旧的浏览器。
响应超时
在 Axios 中设置超时的简单性是一些开发人员的原因之一。在Axios中,您可以使用可选的超时属性,config 对象设置请求中止前的毫秒数。例如:
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()构造函数,它允许我们稍后中止请求。singal是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没有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" 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 格式返回图像数据编码后的字符串,然后插入到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()。接下来,处理响应通过使用异步函数,如下所示:
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 在一个紧凑的包中为您的大部分 HTTP 提供了一个易于使用的 API,以满足痛心的需要。但是,如果您更喜欢坚持使用本机 API,则没有什么阻止您实现 Axios 功能。正如本文所讨论的,完全有可能重现使用Web 浏览器提供的fetch()方法的 Axios 库。
最终,是否值得加载客户端 HTTP API 取决于您对使用内置 API 感到很自在。
(本文由闻数起舞翻译自LogRocket的文章《Axios or fetch(): Which should you use?》,转载请注明出处,原文链接:https://blog.logrocket.com/axios-or-fetch-api/)