上一篇文章中我简单的介绍了下vue的动态路由Vue动态路由的实现(基于vue-cli3),这篇文章我们就来好好说说axios的封装复用,有需要的朋友可以做一下参考。有什么错误还望指出,大神勿喷。


axios简介

自从Vue2.0推荐大家使用axios开始,axios被越来越多的人所了解。下面我们先来说说什么是axios。

基于promise用于浏览器和node.js的http客户端

axios的特点是什么呢?

  • 支持浏览器和node.js
  • 支持promise
  • 能拦截请求和响应
  • 能转换请求和响应数据
  • 能取消请求
  • 自动转换JSON数据
  • 浏览器端支持防止CSRF(跨站请求伪造)

封装axios

关于axios的引用、安装等步骤在这里不做具体描述,感兴趣的小伙伴可以去看看我的github,有一个详细的Demo,地址在本文最后。
好了,废话不多说,直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import axios from 'axios';
import { Message } from 'ant-design-vue';
import { getHost } from '@/utils/common';
import router from '@/router';
// 获取后端请求接口,需要在配置文件中配置
// 如果使用proxyTable代理时去掉,直接修改axios实例里面的baseURL即可
const baseUrl = getHost('VUE_APP_BASE_API');

// 创建一个axios实例
const service = axios.create({
baseURL: baseUrl,
// 超时时间
timeout: process.env['AXIOS_TIMEOUT'],
// 允许请求时携带cookie
withCredentials: true
});

const queue = [];
// api 内置的中断ajax方法
const cancelToken = axios.CancelToken;
const requestUrl = config => {
return `${config.url}_${config.method}`;
};

const removeQueue = config => {
queue.forEach((item, index) => {
if (item.requestUrl === requestUrl(config)) {
item.cancel();
queue.splice(index, 1);
}
});
};

// axios拦截器
service.interceptors.request.use(
config => {
// 中断重复请求
removeQueue(config);
config.cancelToken = new cancelToken(c => {
queue.push({
requestUrl: requestUrl(config),
cancel: c
});
});
return config;
},
error => {
return Promise.reject(error);
}
);

// axios响应拦截器
service.interceptors.response.use(
response => {
// 请求完成后从队列中删除
removeQueue(response.config);
if (response.status === 200) {
return response.data;
}
},
error => {
if (error.response.status === 401) {
router.push({ name: 'login' });
} else {
Message.error(error.response.data.message);
}
return Promise.reject(error);
}
);

export default service;

在封装axios的过程中,我引用了一个请求队列来过滤重复的请求。第一次请求的时候,向队列中添加一个标识(我用的是接口的url加上请求方法),每次成功接收到返回值后,去除该队列中的标识,如果该接口还未成功,这时又请求一个相同的接口,使用axios取消请求的方式去除队列中的上次接口。
更完美的做法是还可以添加一个缓存队列用来解决高并发的问题,但逻辑比较复杂,目前项目中也用不到,以后有机会再补上。

请求配置

axios在使用的过程中拥有丰富的配置项,下面是所有可用的请求配置项,只有url是必填,默认的请求方法是GET,如果没有指定请求方法的话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
{
// `url` 是请求的接口地址
url: '/user',

// `method` 是请求的方法
method: 'get', // 默认值

// 如果url不是绝对路径,那么会将baseURL和url拼接作为请求的接口地址
// 用来区分不同环境,建议使用
baseURL: 'https://some-domain.com/api/',

// 用于请求之前对请求数据进行操作
// 只用当请求方法为‘PUT’,‘POST’和‘PATCH’时可用
// 最后一个函数需return出相应数据
// 可以修改headers
transformRequest: [function (data, headers) {
// 可以对data做任何操作

return data;
}],

// 用于对相应数据进行处理
// 它会通过then或者catch
transformResponse: [function (data) {
// 可以对data做任何操作

return data;
}],

// `headers` are custom headers to be sent
headers: {'X-Requested-With': 'XMLHttpRequest'},

// URL参数
// 必须是一个纯对象或者 URL参数对象
params: {
ID: 12345
},

// 是一个可选的函数负责序列化`params`
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function(params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},

// 请求体数据
// 只有当请求方法为'PUT', 'POST',和'PATCH'时可用
// 当没有设置`transformRequest`时,必须是以下几种格式
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
data: {
firstName: 'Fred'
},

// 请求超时时间(毫秒)
timeout: 1000,

// 是否携带cookie信息
withCredentials: false, // default

// 统一处理request让测试更加容易
// 返回一个promise并提供一个可用的response
// 其实我并不知道这个是干嘛的!!!!
// (see lib/adapters/README.md).
adapter: function (config) {
/* ... */
},

// `auth` indicates that HTTP Basic auth should be used, and supplies credentials.
// This will set an `Authorization` header, overwriting any existing
// `Authorization` custom headers you have set using `headers`.
auth: {
username: 'janedoe',
password: 's00pers3cret'
},

// 响应格式
// 可选项 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // 默认值是json

// `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
xsrfCookieName: 'XSRF-TOKEN', // default

// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default

// 处理上传进度事件
onUploadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
},

// 处理下载进度事件
onDownloadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
},

// 设置http响应内容的最大长度
maxContentLength: 2000,

// 定义可获得的http响应状态码
// return true、设置为null或者undefined,promise将resolved,否则将rejected
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},

// `maxRedirects` defines the maximum number of redirects to follow in node.js.
// If set to 0, no redirects will be followed.
// 最大重定向次数?没用过不清楚
maxRedirects: 5, // default

// `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
// and https requests, respectively, in node.js. This allows options to be added like
// `keepAlive` that are not enabled by default.
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),

// 'proxy' defines the hostname and port of the proxy server
// Use `false` to disable proxies, ignoring environment variables.
// `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and
// supplies credentials.
// This will set an `Proxy-Authorization` header, overwriting any existing
// `Proxy-Authorization` custom headers you have set using `headers`.
// 代理
proxy: {
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},

// `cancelToken` specifies a cancel token that can be used to cancel the request
// (see Cancellation section below for details)
// 用于取消请求?又是一个不知道怎么用的配置项
cancelToken: new CancelToken(function (cancel) {
})
}

全局修改axios默认配置

1
2
3
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

实例默认配置

1
2
3
4
5
6
7
// 创建实例时修改配置
var instance = axios.create({
baseURL: 'https://api.example.com'
});

// 实例创建之后修改配置
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

实例配置的优先级

配置项通过一定的规则合并,request config > instance.defaults > 系统默认,优先级高的覆盖优先级低的。

1
2
3
4
5
6
7
8
9
10
// 创建一个实例,这时的超时时间为系统默认的 0
var instance = axios.create();

// 通过instance.defaults重新设置超时时间为2.5s,因为优先级比系统默认高
instance.defaults.timeout = 2500;

// 通过request config重新设置超时时间为5s,因为优先级比instance.defaults和系统默认都高
instance.get('/longRequest', {
timeout: 5000
});

axios使用

个人在写代码时喜欢将接口抽取出来放在一个单独的文件中。虽然加重了一部分代码量,但是对于后期的维护更加方便。当然这只是我个人的习惯,There are a thousand Hamlets in a thousand people’s eyes.
比如说我们在调用登录接口的时候
api/auth/login.js

1
2
3
4
5
6
7
export function LoginByUserName(userLoginObj) {
return axios({
url: '/api/auth/login',
method: 'post',
data: userLoginObj
});
}

先将接口定义在login.js这个文件中,然后在使用的时候直接调用就可以了
store/module/user.js(vuex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { LoginByUserName } from '@/api/auth/login';
...
actions: {
LoginByUserName({ commit }, userLoginByName) {
return new Promise((resolve, reject) => {
LoginByUserName(userLoginByName)
.then(res => {
if (res.code === '111111') {
commit('SET_TOKEN', res.token);
}
resolve(res);
})
.catch(error => {
reject(error);
});
});
}
}

在其它地方使用类似,具体使用可以参考我的github,哪里我已经准备好了一个完整demo vue-config-web,欢迎star。


本文完