Axios 实例
lib/core/Axios.js
首先是 Axios 的构造函数:1
2
3
4
5
6
7
8function Axios(instanceConfig) {
this.defaults = instanceConfig;
// request & response 拦截器
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
this.defaults 存放默认配置。
this.interceptors 存放 request 与 response 的拦截器,我们看下拦截器的实现:
拦截器
1 | lib/core/InterceptorManager.js |
拦截器比较简单,this.handlers 用于存放拦截器,use 用于添加拦截器,eject 用于删除拦截器,forEach 用于迭代所有拦截器。
Axios.prototype.request 方法
1 | Axios.prototype.request = function request(config) { |
我们从定义 chain 这个变量开始看起:1
var chain = [dispatchRequest, undefined];
chain 是一个数组,数组中有两个元素 dispatchRequest 和 undefined,dispatchRequest 我们姑且可以简单的认为是一个返回 Promise 的请求方法。
接下来定义了变量 promise。传入 config 参数:1
var promise = Promise.resolve(config);
之后分别遍历 request 拦截器、response 拦截器,将每一个 request 拦截器的 fulfilled 与 rejected 方法添加到 chain 这个数组的前面,将每一个 response 拦截器的 fulfilled 与 rejected 方法添加到 chain 的后面。
之后 while 循环,当 chain 还有 length 时候,就会一直取出 chain 的前两个元素,再将它们作为参数执行 promise.then(元素0,元素1),并将其赋值给 promise,最后返回 promise。
初看 request 方法时,懵逼就懵逼再这里。
我们将其简化一下,不考虑所有拦截器的情况:
1 | var config = { url: 'test' } // 配置 |
axios 充分利用了 Promise 的特性,Promise 也会返回一个 Promise、Promise.resolve 的参数会传递给 then 的 fulfilled 方法,使得我们在 testRequest 中也可以得到 config,当 testRequest 成功后,再执行用户自定义的 then 方法。
接下来我们看有拦截器的情况
这里我们假设 request、response拦截器各定义了两个,那么遍历每一个拦截器之后的 chain 就是这个样子的:[request成功拦截, request失败拦截, request成功拦截,request失败拦截, testRequest, undefined, response成功拦截, response失败拦截, response成功拦截, response失败拦截]
之后执行 while 循环,当 chain 还有 length 时候,就会一直移除 chain 的前两个元素,并将它们作为参数执行 promise.then(元素0,元素1)
chain 就会变成下面的样子:[request成功拦截,request失败拦截, testRequest, undefined, response成功拦截, response失败拦截, response成功拦截, response失败拦截]
[testRequest, undefined, response成功拦截, response失败拦截, response成功拦截, response失败拦截]
当 while 遍历到 testRequest 则会发起请求,resolve 向下传递数据就由 config 修改为了 response。[response成功拦截, response失败拦截, response成功拦截, response失败拦截]
[response成功拦截, response失败拦截]
[]
当 chain 长度为0后,就会去执行用户定义的 then 方法。
整个过程大致可以用以下代码来描述: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
42var config = { url: 'test' } // 配置参数
function testRequest (config) { // 模拟请求
console.log('testRequest', config)
return new Promise((resolve) => {
// 请求返回数据
var res = { status: 200, data:'I am' }
setTimeout(() => { resolve(res) },3000)
})
}
// request 拦截器的成功函数
var requestInterceptorsFulfilled = function (config) {
console.log('原始config:', config, '我会修改 config 后返回')
config.url = config.url + '~!~'
return config
}
// response 拦截器的成功函数
var responseInterceptorsFulfilled = function (response) {
console.log('原始response:', response, '我会修改 response 后返回')
response.data = response.data + ' OK'
return response
}
// request、response错误处理
var requestInterceptorsRejected = responseInterceptorsRejected = function (error) {
return Promise.reject(error);
}
// 执行链
var chain = [
requestInterceptorsFulfilled, requestInterceptorsRejected,
requestInterceptorsFulfilled, requestInterceptorsRejected,
testRequest, undefined,
responseInterceptorsFulfilled, responseInterceptorsRejected,
responseInterceptorsFulfilled, responseInterceptorsRejected
]
var promise = Promise.resolve(config)
// 循环执行
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
// 用户自定义then方法:axios.get().then() ⬇
promise.then((res) => {
console.log('用户最终拿到的数据', res)
})
我们执行这段代码,可以在 console 面板中看到输出:1
2
3
4
5
6
7
8
9
10原始config: {url: "test"} 我会修改 config 后返回 // 此处第一个 request 拦截器对 config 进行修改
原始config: {url: "test~!~"} 我会修改 config 后返回 // 此处第二个 request 拦截器对 config 进行修改
testRequest {url: "test~!~~!~"} // 执行 testRequest 函数,此处会等待三秒,可以看到 config 已经被修改了两次,变为 {url: "test~!~~!~"}
Promise {<pending>} // pending 状态的 Promise
原始response: {status: 200, data: "I am"} 我会修改 response 后返回 // 请求返回了数据,response 拦截器对 response 进行修改
原始response: {status: 200, data: "I am OK"} 我会修改 response 后返回 // 请求返回了数据,response 拦截器对 response 进行修改
用户最终拿到的数据 {status: 200, data: "I am OK OK"} // 这里是用户自定义的 then 中拿到的结果
request 方法是 axios 的核心之一,初看有些懵逼,但真正看明白了,会让人直呼妙啊!妙啊!
其他
定义 getUri 方法
1 | Axios.prototype.getUri = function getUri(config) { |
添加 axios 支持的方法的别名
这一步使得用户可以以 axios.get()、axios.post() 的方式调用。
别名的定义分为两种,区别在于传参的方式不同。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// 添加支持的方法的别名
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, data, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url,
data: data
}));
};
});