coder-Tom

一个喜欢代码, 音乐,读书, 跑步, 记录生活的程序员

0%

axios源码分析

axios源码分析

打开之后, 它算是一个工程,里面有很多文件夹和文件,同学们可以一边看源码一边看我的分析。

image-20220930144530311

我这里列出一些重点的:

image-20220930144954851

先看看axios.js文件中的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict';
启用严格模式
axios入口文件
import utils from './utils.js';
引入工具
import bind from './helpers/bind.js';
引入绑定函数, 创建函数
import Axios from './core/Axios.js';
引入Axios主文件
import mergeConfig from './core/mergeConfig.js';
引入合并配置的函数
import defaults from './defaults/index.js';
导入默认配置

axios怎么来的,我们可以清楚, axios是通过createInstance创建而来的,下面我们来看看createInstance是怎么运作的

我们先打开lib/core/Axios.js文件:

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
'use strict';

import utils from './../utils.js';
import buildURL from '../helpers/buildURL.js';
import InterceptorManager from './InterceptorManager.js';
import dispatchRequest from './dispatchRequest.js';
import mergeConfig from './mergeConfig.js';
import buildFullPath from './buildFullPath.js';
import validator from '../helpers/validator.js';
import AxiosHeaders from './AxiosHeaders.js';

const validators = validator.validators;

/**
* Create a new instance of Axios
*
* @param {Object} instanceConfig The default config for the instance
*
* @return {Axios} A new instance of Axios
*/
开始对axios进行构造
class Axios {
constructor(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}

/**
* Dispatch a request
*
* @param {String|Object} configOrUrl The config specific for this request (merged with this.defaults)
* @param {?Object} config
*
* @returns {Promise} The Promise to be fulfilled
*/
request(configOrUrl, config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
if (typeof configOrUrl === 'string') {
config = config || {};
config.url = configOrUrl;
} else {
config = configOrUrl || {};
}

config = mergeConfig(this.defaults, config);

const transitional = config.transitional;

if (transitional !== undefined) {
validator.assertOptions(transitional, {
silentJSONParsing: validators.transitional(validators.boolean),
forcedJSONParsing: validators.transitional(validators.boolean),
clarifyTimeoutError: validators.transitional(validators.boolean)
}, false);
}

// Set config.method
config.method = (config.method || this.defaults.method || 'get').toLowerCase();

// Flatten headers
const defaultHeaders = config.headers && utils.merge(
config.headers.common,
config.headers[config.method]
);

defaultHeaders && utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);

config.headers = new AxiosHeaders(config.headers, defaultHeaders);

// filter out skipped interceptors
const requestInterceptorChain = [];
let synchronousRequestInterceptors = true;
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
return;
}

synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;

requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});

const responseInterceptorChain = [];
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});

let promise;
let i = 0;
let len;

if (!synchronousRequestInterceptors) {
const chain = [dispatchRequest.bind(this), undefined];
chain.unshift.apply(chain, requestInterceptorChain);
chain.push.apply(chain, responseInterceptorChain);
len = chain.length;

promise = Promise.resolve(config);

while (i < len) {
promise = promise.then(chain[i++], chain[i++]);
}

return promise;
}

len = requestInterceptorChain.length;

let newConfig = config;

i = 0;

while (i < len) {
const onFulfilled = requestInterceptorChain[i++];
const onRejected = requestInterceptorChain[i++];
try {
newConfig = onFulfilled(newConfig);
} catch (error) {
onRejected.call(this, error);
break;
}
}

try {
promise = dispatchRequest.call(this, newConfig);
} catch (error) {
return Promise.reject(error);
}

i = 0;
len = responseInterceptorChain.length;

while (i < len) {
promise = promise.then(responseInterceptorChain[i++], responseInterceptorChain[i++]);
}

return promise;
}

往axios原型上添加getUri方法
getUri(config) {
config = mergeConfig(this.defaults, config);
const fullPath = buildFullPath(config.baseURL, config.url);
return buildURL(fullPath, config.params, config.paramsSerializer);
}
}

// Provide aliases for supported request methods
utils.forEach是专门用来遍历数组的
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
往axios原型上面加方法,分别是delete,get, head,options
Axios.prototype[method] = function(url, config) {
return this.request(mergeConfig(config || {}, {
method,
url,
data: (config || {}).data
}));
};
});
我们这里的post, put ,patch也是如此
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
/*eslint func-names:0*/

function generateHTTPMethod(isForm) {
return function httpMethod(url, data, config) {
return this.request(mergeConfig(config || {}, {
method,
headers: isForm ? {
'Content-Type': 'multipart/form-data'
} : {},
url,
data
}));
};
}

Axios.prototype[method] = generateHTTPMethod();

Axios.prototype[method + 'Form'] = generateHTTPMethod(true);
});
运行完该文件后, axios原型上面就多了很多方法,之后我们axios的实例对象就可以调用这些方法,接着就是导出这个原型对象
export default Axios;

再来看看lib/axios.js

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
'use strict';

import utils from './utils.js';
import bind from './helpers/bind.js';
import Axios from './core/Axios.js';
import mergeConfig from './core/mergeConfig.js';
import defaults from './defaults/index.js';
import formDataToJSON from './helpers/formDataToJSON.js';
import CanceledError from './cancel/CanceledError.js';
import CancelToken from'./cancel/CancelToken.js';
import isCancel from'./cancel/isCancel.js';
import {VERSION} from './env/data.js';
import toFormData from './helpers/toFormData.js';
import AxiosError from './core/AxiosError.js';
import spread from './helpers/spread.js';
import isAxiosError from './helpers/isAxiosError.js';

/**
* Create an instance of Axios
*
* @param {Object} defaultConfig The default config for the instance
*
* @returns {Axios} A new instance of Axios
*/
function createInstance(defaultConfig) {
const context = new Axios(defaultConfig);
const instance = bind(Axios.prototype.request, context);

// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context, {allOwnKeys: true});

// Copy context to instance
utils.extend(instance, context, null, {allOwnKeys: true});

// Factory for creating new instances
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig, instanceConfig));
};

return instance;
}

// Create the default instance to be exported
const axios = createInstance(defaults);

// Expose Axios class to allow class inheritance
axios.Axios = Axios;

// Expose Cancel & CancelToken
axios.CanceledError = CanceledError;
axios.CancelToken = CancelToken;
axios.isCancel = isCancel;
axios.VERSION = VERSION;
axios.toFormData = toFormData;

// Expose AxiosError class
axios.AxiosError = AxiosError;

// alias for CanceledError for backward compatibility
axios.Cancel = axios.CanceledError;

// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
};

axios.spread = spread;

// Expose isAxiosError
axios.isAxiosError = isAxiosError;

axios.formToJSON = thing => {
return formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing);
};

export default axios;

image-20221002153656425

换句话说, 当Axios.js执行完之后, context = new Axios(defaultConfig)这里的context就已经有了上面配置的对象和方法

image-20221002154145857

这就是为什么我们在使用axios的时候, 既可以把他当成函数去用, 也可以直接调用上面的方法,我们可以直接去axios.post()或者是axios.requst(), 这个就是原因。

image-20221002154532596

utils.extend()这个是精髓, 它可以将对象的方法进行复制,图中,utils.extend(instance, context),就是将实例对象的方法拓展到instance身上,我们刚才看了,Axios.prototype上面有着很多的方法, 而我们instance这里是一个函数, 然后给他上面添加这些方法, 就是我们在创建instace实例的时候 , 我们一直强调, 它是一个函数并非对象, 这样就解释的通了