Vue中封装axios的取消请求事情
# Vue中封装axios的取消请求事情
# 前言
需要取消重复请求的场景:
- 比如输入框搜索需要取消上一次的重复请求
- tab切换频繁获取数据列表接口时,接口重复请求
- 就是用户频繁切换操作时,我们调用都是同一个接口的时候,就需求先取消上一次的接口请求,只请求用户最后一次操作的接口,不然相同的接口重复请求,接口是异步的,到时就容易拿到数据不是最后一次操作想要的数据了
# axios本身就封装了取消事件
axios中文文档 取消事件 (opens new window)
# axios取消事件代码如下
- 方法一、可以使用 CancelToken.source 工厂方法创建 cancel token,像这样:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 方法二、还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// cancel the request
cancel();
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 单独使用axios的时候,两个方法都可以,但是封装好axios方法在vue中全局调用时,方法一有个问题,axios的cancel方法会把即将要发出的请求取消掉,所以用第二个方法
# 完整axios封装代码如下:
import axios from 'axios'
import UserModel from '../models/user'
// 接口前缀
const PREFIX = {
mock: 'https://yapi.comliq.net/mock/31/', // yapi mock 地址
development: 'http://xxx/', // qa环境
production: 'http://xxx/', // 生产环境
}
const env = process.env.NODE_ENV
let interfacePrefix = PREFIX[env] || PREFIX.production // 接口前缀
/**
* config 自定义配置项
* @param withoutCheck 不使用默认的接口状态校验,直接返回 response
* @param returnOrigin 是否返回整个 response 对象,为 false 只返回 response.data
* @param mock 是否使用 mock 服务
* @param timeout 接口请求超时时间,默认10秒
* @param isCancelRequest 是否可以取消请求
*/
const configDefault = {
returnOrigin: false,
withoutCheck: false,
mock: false,
timeout: 10000
}
// 创建请求器
const service = axios.create(Object.assign({
baseURL: '',
responseType: 'json',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
}, configDefault))
// 添加请求拦截器
service.interceptors.request.use(
request => {
const reqData = request.data || request.params
if (reqData && !request.canEmpty) { // 对请求参数进行处理,清除空参数
request.data = deleteEmpty(reqData)
}
// 检测接口,根据环境自动切换前缀
if (request.url.indexOf('http') === -1) {
if (request.url[0] === '/') {
request.url = request.url.substr(1)
}
request.url = `${env !== 'production' && request.mock ? PREFIX.mock : interfacePrefix}${request.url}`
}
// 若有做鉴权token,需要请求头自动加上token, 这个token自己封装获取的
request.headers.accessToken = UserModel.getToken()
return request
},
error => {
return Promise.reject(error)
},
)
// object对象存放每次new CancelToken生成的方法
let source = {}
// 每次请求前都会把path放在此数组中,响应成功后清除此请求path
let requestList = []
// 定义取消方法
function cancelRequest(path, allCancel) {
// 请求列表里存在此path,即发起重复请求,把之前的请求取消掉
if (path && requestList.includes(path) && typeof source[path] === 'function') {
source[path]('终止请求')
} else if (!path && allCancel) {
// allCancel为true则请求列表里的请求全部取消
requestList.forEach(el => {
source[el]('批量终止请求')
})
}
}
// 添加响应拦截器
service.interceptors.response.use(
res => {
// 获取请求的api
const path = JSON.stringify(res.config.url)
// 请求完成后,将此请求从请求列表中移除
requestList = requestList.filter(item => !path.includes(item))
// HTTP 状态码 2xx 状态入口,data.code 为 200 表示数据正确,无任何错误
},
error => { // 非 2xx 状态入口
return Promise.reject(error)
},
)
// 这里只做post封装演示,大家可以自己封装其他请求方法
function requestFn(method, path, params = {}, options = {}) {
// 取消上一次请求
if (requestList.length) {
cancelRequest(path)
}
// 设置isCancelRequest为ture, 请求前将path推入requestList
if (options.isCancelRequest) {
requestList.push(path)
}
if (method === 'post') {
return service.post(path, params, {
cancelToken: new axios.CancelToken(c => {
source[path] = c
}),
...options
})
}
}
export const api = {
axios: service, // 原始 axios 对象
// 重新封装 get 函数,统一使用方式
get: (path, data, config) => service.get(path, { params: data, ...config }),
delete: (path, data, config) => service.delete(path, { data, ...config }),
post: (path, data, config) => requestFn('post', path, data, config),
put: (path, data, config) => service.put(path, data, config),
}
export default api
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
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
参考文献:
编辑 (opens new window)