快速上手Http库——Axios的使用
前言
Axios是一个当前使用较为广泛,封装的功能较为完备的一个HTTP库,作为目前热门的网络请求库,学习和使用axios中的特性和功能也是前端开发需要具备的能力。本篇文章将对axios的使用和二次封装进行讲解,希望对各位有所帮助。
一、为什么要使用Axios
要想了解为什么要使用axios,我们不妨先说说发送异步请求都有什么方式
(1)Ajax
Ajax的核心是JavaScript对象XmlHttpRequest。该对象在Internet Explorer 5中首次引入,它是一种支持异步请求的技术.简而言之,XmlHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应,而不阻塞用户。但Ajax的缺点也很明显,使用起来复杂,很多东西都需要我们自己做二次封装,所以随着其他二次封装的Http库出现后,我们现在基本都不直接使用Ajax来做网络请求了。
(2)JQuery
JQurey库在网络请求方面对Ajax进行了二次封装,由于JQuery简单易用,功能齐全,降低了我们进行网络请求的的复杂度,在以前很长一段时间中也是很多项目Http库的选择。但随着技术的发展,jQuery 也逐步走向一个衰弱的过程。越来越多的前端开发者开始使用诸如 Angular、React 和 Vue 这样的新型框架。在一个基本用不到 jQuery 的技术中进行前端开发,为了要使用 jQuery 的 Ajax 相关方法而强行引入整个 jQuery,这就有些得不偿失,所以人们寻求体积更小、更加轻便的类库。
(3)fetch
fetch也是当前使用较多的网络请求方式之一,需要注意的是fetch并不是对Ajax的二次封装,而是使用原生的JS进行开发的。fetch是基于 promise 进行设计的,写法上也更加的方便和简单,更为符合关注点分离的原则。但fetch的API 偏底层,需要封装;默认不带Cookie,需要手动添加; 浏览器支持情况不是很友好。
(4)axios
axios在浏览器端实际上也是基于 XMLHttpRequest 来实现的,并且基于 promise 提供了一套 promise 风格的链式调用 API,具备着支持promise API、支持请求和响应拦截、提供并发请求接口功能、轻量高效、简单易用、客户端支持防止CSRF等优点。也是当前使用最为广泛的类库之一。
综上,我们可以看见对于应用Vue、React等框架的项目来说,使用fetch和axios都是较为合理的选择,但axios的兼容性和功能性暂时还是要略胜一筹,这也是我们为什么要在项目中使用axios的原因。
要想在项目中使用axios,我们需要先在项目中安装axios类库
npm install axios
二、axios的使用
(一)axios的请求方式
axios提供了下面2种方式来让我们创建对应的请求
axios(config)
axios(url[, config])
我们可以看一下下面这个演示用例:
config中配置的参数是有关本次请求的详细属性,常见的需要配置的是url,method,以及params(get请求)或者data(post请求)。需要注意的是,axios默认的请求方式是get,所以如果是get请求,method可以省略。
axios({
url: 'http://httpbin.org/get',
method: 'get',
params: {
name: '小明',
age: 14
}
}).then(res => {
console.log(res)
}).catch(err => console.error)
等价于下面这种写法:
axios('http://httpbin.org/get',{
params: {
name: '小明',
age: 14
}
}).then(res => {
console.log(res)
}).catch(err => console.error)
我们在浏览器的控制台查看axios返回的response结果,可以看到axios帮助我们把请求和响应结果都做了一个封装,实际上除了data
属性外,其他的属性都是axios为了让我们更好地分析本次网络请求的参数而做的封装,一般来说我们更多是直接使用data
中的数据即可。
除了上面两种封装方式,我们还可以用axios提供的简写方式来快速创建请求:
##### axios.request(config)
##### axios.get(url[, config])
##### axios.delete(url[, config])
##### axios.head(url[, config])
##### axios.post(url[, data[, config]])
##### axios.put(url[, data[, config]])
##### axios.patch(url[, data[, config]])
其实,上述的简写方式以及前面提到的axios(config)
这种创建请求的方式,本质上调用的都是axios.request(config)
这个函数,有兴趣的同学可以自行看一下axios的源码。
然后下面我们就来使用简写方式做一个小案例吧:
使用简写方式发送一个post
请求,post
请求要求我们提供3个参数,分别是url、data和config,如果不需要额外配置config则可以不写,使用默认值:
axios.post('http://httpbin.org/post',{name:'小明',age:14}).then(res=>{
console.log(res)
}).catch(err => console.error(err));
我们可以看一下简写方式响应的结果,其实和之前基本上是差不多的,只是说简写方式让我们用起来更加方便而已。
(二)axios结果的处理
axios执行完网络请求后,返回的结果是一个Promise
对象,常见的我们可以使用then
、catch
来分别处理请求成功和失败的结果。
axios.get('http://httpbin.org/get',{
params:{
name:'小明',
age:14
}
}).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
})
我们还可以使用ES7规范提出的await
、async
关键字来解决异步的问题:
需要注意的是,使用await关键字的话,方法所在的函数需要加上async
async function xxx(){
try{
const result = await axios.get('http://httpbin.org/get', {
params: {
name: '小明',
age: 14
}
})
console.log(result);
}catch (err) {
console.log(err);
}
}
(三)axios.all()
的使用
axios支持我们使用axios.all()
来同时处理多个异步请求的场景:
比如下面我们想要在request1和request2都请求完之后,对结果进行输出:
const request1 = axios.get('http://httpbin.org',{
params:{
name: '小明',
age: 14
}
});
const request2 = axios.get('http://httpbin.org',{
params:{
name: '小蓝',
age: 15
}
});
axios.all([request1,request2]).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
})
如果觉得返回对象是一个数组不好处理,我们可以通过数组的解构赋值来解决这个问题:
axios.all([request1,request2]).then(([res1,res2])=>{
console.log(res1);
console.log(res2);
}).catch(err=>{
console.log(err);
})
三、axios的配置
(一)请求配置选项
我们在一开始讲axios的API时,就提到了axios(config)
这种创建方式,那么请求具体可以传入哪些配置呢?
其实axios官方对这一块已经讲得挺多的了,这里的话我就摘抄部分重要的出来吧:
{
// `url` 是用于请求的服务器 URL
url: '/user',
// `method` 是创建请求时使用的方法
method: 'get', // default
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `headers` 是即将被发送的自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` 是即将与请求一起发送的 URL 参数
// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
params: {
ID: 12345
},
// `paramsSerializer` 是一个负责 `params` 序列化的函数
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function(params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// `data` 是作为请求主体被发送的数据
// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 `transformRequest` 时,必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
data: {
firstName: 'Fred'
},
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // default
proxy: {
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
}
}
(二)响应结构信息
响应结构指的是axios完成网络请求后,返回的数据格式,我们在之前的讲解中就有大概讲过。这里的话我们可以看一下摘抄自官方的各个数据的描述。
{
// `data` 由服务器提供的响应
data: {},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {},
// 'request'
// `request` is the request that generated this response
// It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance the browser
request: {}
}
(三)全局默认配置
全局默认配置是十分重要的知识点,其实很多时候项目中的请求基本上大部分配置都是一样的,比如baseURL
、timeout
和content-type
等,如果我们每次发请求的时候都要手动写,那就很麻烦了。所以axios为我们提供了一种全局的配置,我们可以对axios进行一些默认请求的配置,这样后续再请求的时候,我们就不需要再手写baseURL等参数了。
如果不知道可以配置哪些默认配置,可以看一下上面的请求配置或者官方文档。
axios.defaults.baseURL = 'http://httpbin.org';
axios.defaults.timeout = 5000;
axios.defaults.headers.common['Authorization'] = 'AUTH_TOKEN';
我们在上面配置了三个axios的全局默认配置,现在我们发送一个请求后来观察一下控制台的输出:
我们可以看到,使用了全局默认配置之后,我们再发送请求时,就不需要额外写一些重复性的请求配置了。
(四)自定义实例默认配置
我们前端项目开发中,可能会遇到有多个后端服务器的情况,针对这种情况我们单纯地使用全局默认配置可能就有点不方便了,比如有多个后端服务器所以baseURL就不能只设成一个。这种情况下,axios还支持我们自定义多个实例来满足我们的需要。
自定义实例后对于实例的默认配置有下面2种方式,一般来说,我们用的第一种比较多
// Set config defaults when creating the instance
const instance = axios.create({
baseURL: 'https://api.example.com'
});
// Alter defaults after instance has been created
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
(五)配置的优先顺序
有些读者可能会有疑问,对于timeout
配置如果在多个地方进行了不同的配置,那么会以哪个为准。
实际上配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者。这里是一个例子:
// 使用由库提供的配置的默认值来创建实例
// 此时超时配置的默认值是 `0`
var instance = axios.create();
// 覆写库的超时默认值
// 现在,在超时前,所有请求都会等待 2.5 秒
instance.defaults.timeout = 2500;
// 为已知需要花费很长时间的请求覆写超时设置
instance.get('/longRequest', {
timeout: 5000
});
四、 axios拦截器
axios为我们提供了请求和响应的拦截器,方便我们在请求或响应被 then 或 catch 处理前做一些特殊的处理。实际上,我们在项目中也经常会使用到axios拦截器来做一些譬如请求结果的二次封装等操作。
(一)请求拦截器
我们可能会如下场景中在请求拦截器中做一些操作:
1. 发送网络请求时,在界面的中间位置显示Loading的组件
2. 某一些请求要求用户必须携带token,如果没有携带,那么直接跳转到登录页面
3. 对params/data序列化的操作
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
(二)响应拦截器
对于响应拦截器,我们做的操作一般是根据返回的状态码来做一下不同的处理以及对请求的结果做一下二次的封装。
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
五、axios的二次封装
为什么要对axios做二次封装
在上面的讲解中,相信大家已经知道了axios的使用,但实际开发中,我们并不会直接使用axios来创建网络请求,而是会先对axios做一下二次封装,原因很简单,如果没有对axios做二次封装的话,一旦后面项目需要更改网络请求的类库那么改造的难度就相当大了,我们需要在项目中一点点地把使用到axios的api修改掉,而如果我们一开始就对axios做了二次封装的话,那么其他文件就只需要用我们二次封装的api来操作就行,即使后面项目更改了网络请求的类库,我们也只需要修改二次封装的那个文件就行。
由于不同的公司的二次封装风格不同,这里的话我只简单以个人为例,做些常见配置:
在src目录下创建api
目录,专门用于存放网络请求的api:
其中,config.js
用于存放对axios默认配置的常量,这样方便我们后续查看和修改:
const devBaseURL = 'http://localhost:3000';
const proBaseURL = 'http://xxxx.xxxx.xxxx.xxxx:3000'
export const BASE_URL = process.env.NODE_ENV === 'development'? devBaseURL : proBaseURL;
export const TIMEOUT = 5000;
export const CONTENT_TYPE = 'application/x-www-form-urlencoded';
http.js
用于存放我们对axios的默认配置
/**
* 这个文件用于封装axios
*/
import axios from 'axios';
import {BASE_URL,TIMEOUT,CONTENT_TYPE} from './config';
axios.defaults.baseURL = BASE_URL;
axios.defaults.timeout = TIMEOUT;
axios.defaults.headers.common['Content-Type'] = CONTENT_TYPE;
//响应拦截器
axios.interceptors.response.use(
response => {
//如果reponse里面的status是200,说明访问到接口了,否则错误
if(response.status == 200){
return Promise.resolve(response);
}else{
return Promise.reject(response);
}
},
error => {
if(error.response.status){
return Promise.reject(error.response);
}
}
);
/**
* 封装get方法
*/
export function get(url,params={}){
return new Promise((resolve,reject) => {
axios.get(url,{params:params})
.then(response =>{
resolve(response.data);
})
.catch(err =>{
reject(err);
})
});
}
/**
* 封装post方法
*/
export function post(url,data={},config){
return new Promise((resolve,reject) => {
axios.post(url,data,config)
.then(response =>{
resolve(response.data);
})
.catch(err =>{
reject(err);
})
});
}
index.js
文件用于存放具体的接口
import {get,post} from './http';
// 获取token
export const getTransferToken = (data) => post('/xxx',data) ;
// 获取
export const getAudio = (data) => post('/xxx',data,{responseType: 'blob'}) ;
至此,对于axios的使用我们就介绍到这里了,对于更多细节的学习,大家可以自行参考axios官方:
官网链接:http://www.axios-js.com/zh-cn/docs/