入口
// 导入各种工具函数
var utils = require('./utils');
// 自定义的bind方法 该方法接收2个参数 fn和thisArg
// 返回一个wrap函数 调用该函数会调用apply方法 将fn的this指向修改为thisArg
var bind = require('./helpers/bind');
// Axios构造函数
var Axios = require('./core/Axios');
// 合并配置方法
var mergeConfig = require('./core/mergeConfig');
// 默认配置
var defaults = require('./defaults');
var formDataToJSON = require('./helpers/formDataToJSON');
/**
* Create an instance of Axios
*
* @param {Object} defaultConfig The default config for the instance
*
* @returns {Axios} A new instance of Axios
*/
// 该方法用于创建一个Axios实例
function createInstance(defaultConfig) {
// 使用defaultConfig创建一个axios实例
var context = new Axios(defaultConfig);
// 调用bind方法将Axios原型对象上定义的request方法和context传入
// 此时instance就是一个函数
// 一旦调用instance -> instance() 就等同于Axios.prototype.request.bind(context)()
// 这就是为什么可以直接调用axios
var instance = bind(Axios.prototype.request, context);
// Copy axios.prototype to instance
// 遍历Axios.prototype上的方法 通过bind方法绑定到instance身上 -> instance[key] = bind(Axios.prototype[key], context)
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
// 把context上的默认配置和请求、相应拦截器等拷贝到instance上
utils.extend(instance, context);
// instance = Object.assign(instance, context);
// Factory for creating new instances
// create方法 传入一些自定义配置,返回一个axios实例
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig, instanceConfig));
};
// 返回instance
return instance;
}
// Create the default instance to be exported
var axios = createInstance(defaults);
// Expose Axios class to allow class inheritance
axios.Axios = Axios;
// Expose Cancel & CancelToken
axios.CanceledError = require('./cancel/CanceledError');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
axios.VERSION = require('./env/data').version;
axios.toFormData = require('./helpers/toFormData');
// Expose AxiosError class
axios.AxiosError = require('../lib/core/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 = require('./helpers/spread');
// Expose isAxiosError
axios.isAxiosError = require('./helpers/isAxiosError');
axios.formToJSON = function(thing) {
return formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing);
};
module.exports = axios;
// Allow use of default import syntax in TypeScript
module.exports.default = axios;
bind方法
module.exports = function bind(fn, thisArg) {
// 返回一个wrap函数 调用wrap相当于将fn的this指向thisArg
return function wrap() {
return fn.apply(thisArg, arguments);
};
};
extend方法
function extend(a, b, thisArg) {
// 调用forEach方法遍历b 将b身上的属性key复制给a 如果key的值是个函数 则指向bind方法进行this指向修改
forEach(b, function assignValue(val, key) {
if (thisArg && typeof val === 'function') {
a[key] = bind(val, thisArg);
} else {
a[key] = val;
}
});
return a;
}
核心构造函数 Axios
'use strict';
var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
var InterceptorManager = require('./InterceptorManager');
var dispatchRequest = require('./dispatchRequest');
var mergeConfig = require('./mergeConfig');
var buildFullPath = require('./buildFullPath');
var validator = require('../helpers/validator');
var 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
*/
function Axios(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
*/
// 真正的发送请求的方法
Axios.prototype.request = function request(configOrUrl, config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
// 如果configOrUrl是字符串 则说明是这样调用的 axios('xxx',{})
if (typeof configOrUrl === 'string') {
// config等于config || {}
config = config || {};
// config.url = configOrUrl
config.url = configOrUrl;
} else {
// 否则 有可能是这样调用的 axios({url:'xxx'})
config = configOrUrl || {};
}
// 合并默认配置和用户自定义配置
config = mergeConfig(this.defaults, config);
// Set config.method
// 设置 请求方法,默认 get
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
var 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
);
}
// filter out skipped interceptors
// 定义请求拦截器数组
var requestInterceptorChain = [];
var synchronousRequestInterceptors = true;
// 调用请求拦截器的forEach方法 该方法会遍历请求拦截器的handlers数组 并执行传入的函数 例如下面的unshiftRequestInterceptors方法
this.interceptors.request.forEach(function unshiftRequestInterceptors(
interceptor
) {
if (
typeof interceptor.runWhen === 'function' &&
interceptor.runWhen(config) === false
) {
return;
}
synchronousRequestInterceptors =
synchronousRequestInterceptors && interceptor.synchronous;
// interceptor是一个对象 {
// fulfilled: fulfilled,
// rejected: rejected,
// synchronous: options ? options.synchronous : false,
// runWhen: options ? options.runWhen : null
// }
// 将interceptor的fulfilled和rejected方法添加到requestInterceptorChain头部
// 因为调用的是unshift 所以先定义的请求拦截器后执行
requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 定义响应拦截器数组
var responseInterceptorChain = [];
this.interceptors.response.forEach(function pushResponseInterceptors(
interceptor
) {
// 将interceptor的fulfilled和rejected方法添加到requestInterceptorChain尾部
// 因为调用的是push 所以先定义的响应拦截器先执行
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});
var promise;
// 若需要异步执行,则异步执行拦截器数组
if (!synchronousRequestInterceptors) {
// 组成Promise调用链
// 把 xhr 请求 的 dispatchRequest 和 undefined 放在一个数组里 方便后面调用
var chain = [dispatchRequest, undefined];
// 将requestInterceptorChain添加到chain的头部
Array.prototype.unshift.apply(chain, requestInterceptorChain);
// 将responseInterceptorChain添加到chain的尾部
chain = chain.concat(responseInterceptorChain);
// 创建 一个resolve(config)的Promise 实例 此时通过then方法拿到的值就是config
promise = Promise.resolve(config);
// 遍历chain 挨个执行拦截器中的fulfilled和rejected函数
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
// 返回promise 所以用户可以axios().then()调用
return promise;
}
// 否则同步执行请求拦截器,直到拦截器栈清空
var newConfig = config;
// 先执行请求拦截器
while (requestInterceptorChain.length) {
// 成功的回调
var onFulfilled = requestInterceptorChain.shift();
// 失败的回调
var onRejected = requestInterceptorChain.shift();
try {
// 获取请求拦截器处理后的config 这就是为什么onFulfilled要返回config
newConfig = onFulfilled(newConfig);
} catch (error) {
// 一旦走到失败回调 直接break
onRejected(error);
break;
}
}
// 再发送请求
try {
// 真正开始发送请求 将请求拦截器处理后的config传递给dispatchRequest
promise = dispatchRequest(newConfig);
} catch (error) {
return Promise.reject(error);
}
// 最后再执行响应拦截器
while (responseInterceptorChain.length) {
promise = promise.then(
responseInterceptorChain.shift(),
responseInterceptorChain.shift()
);
}
// 返回promise 所以用户可以axios().then()调用
return promise;
};
// 获取最终请求的uri
Axios.prototype.getUri = function getUri(config) {
config = mergeConfig(this.defaults, config);
var fullPath = buildFullPath(config.baseURL, config.url);
return buildURL(fullPath, config.params, config.paramsSerializer);
};
// 将'delete', 'get', 'head', 'options'等方法添加到Axios.prototype上 本质是还是调用request方法
// Provide aliases for supported request methods
utils.forEach(
['delete', 'get', 'head', 'options'],
function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(
mergeConfig(config || {}, {
method: method,
url: url,
data: (config || {}).data
})
);
};
}
);
// 将'post', 'put', 'patch'等方法添加到Axios.prototype上 本质是还是调用request方法
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: method,
// 判断是否是提交表单数据
headers: isForm
? {
'Content-Type': 'multipart/form-data'
}
: {},
url: url,
data: data
})
);
};
}
// 调用一下generateHTTPMethod方法 返回httpMethod方法
Axios.prototype[method] = generateHTTPMethod();
// 调用一下generateHTTPMethod方法
Axios.prototype[method + 'Form'] = generateHTTPMethod(true);
});
module.exports = Axios;
拦截器InterceptorManager
'use strict';
var utils = require('./../utils');
function InterceptorManager() {
// 拦截器数组
this.handlers = [];
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
// 添加拦截器
InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
// fulfilled 成功的回调函数
// rejected 失败的回调函数
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected,
synchronous: options ? options.synchronous : false,
runWhen: options ? options.runWhen : null
});
// 返回索引
return this.handlers.length - 1;
};
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*
* @returns {Boolean} `true` if the interceptor was removed, `false` otherwise
*/
// 移除拦截器
InterceptorManager.prototype.eject = function eject(id) {
// 根据id找到拦截器,这里并没有直接删除,而是赋值为null 是为了防止id错乱
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
/**
* Clear all interceptors from the stack
*
* @returns {void}
*/
// 清空拦截器
InterceptorManager.prototype.clear = function clear() {
if (this.handlers) {
this.handlers = [];
}
};
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*
* @returns {void}
*/
// 遍历handlers,传递一个回调函数(handlers中每一项作为函数的参数调用)
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
module.exports = InterceptorManager;
dispatchRequest
'use strict';
var utils = require('./../utils');
var transformData = require('./transformData');
var isCancel = require('../cancel/isCancel');
var defaults = require('../defaults');
var CanceledError = require('../cancel/CanceledError');
var normalizeHeaderName = require('../helpers/normalizeHeaderName');
/**
* Throws a `CanceledError` if cancellation has been requested.
*
* @param {Object} config The config that is to be used for the request
*
* @returns {void}
*/
// 抛出 错误原因,使`Promise`走向`rejected`
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
if (config.signal && config.signal.aborted) {
throw new CanceledError();
}
}
/**
* Dispatch a request to the server using the configured adapter.
*
* @param {object} config The config that is to be used for the request
*
* @returns {Promise} The Promise to be fulfilled
*/
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// 确保 headers 存在
// Ensure headers exist
config.headers = config.headers || {};
// Transform request data
// 转换请求的数据
config.data = transformData.call(
config,
config.data,
config.headers,
null,
config.transformRequest
);
// 拍平 headers
normalizeHeaderName(config.headers, 'Accept');
normalizeHeaderName(config.headers, 'Content-Type');
// Flatten headers
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers
);
// 以下这些方法 删除 headers
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
// 适配器模式 一般是调用xhrAdapter 这里才是真正的发送网络请求的地方
var adapter = config.adapter || defaults.adapter;
// 调用adapter 传入config adapter返回一个promise 在adapter使用Promise包裹一个XMLHttpRequest请求,如果成功则resolve 失败则reject
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
// 转换响应的数据
// Transform response data
response.data = transformData.call(
config,
response.data,
response.headers,
response.status,
config.transformResponse
);
// 返回response
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData.call(
config,
reason.response.data,
reason.response.headers,
reason.response.status,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
xhrAdapter
'use strict';
var utils = require('./../utils');
var settle = require('./../core/settle');
var cookies = require('./../helpers/cookies');
var buildURL = require('./../helpers/buildURL');
var buildFullPath = require('../core/buildFullPath');
var parseHeaders = require('./../helpers/parseHeaders');
var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
var transitionalDefaults = require('../defaults/transitional');
var AxiosError = require('../core/AxiosError');
var CanceledError = require('../cancel/CanceledError');
var parseProtocol = require('../helpers/parseProtocol');
var platform = require('../platform');
module.exports = function xhrAdapter(config) {
// 返回一个promise
return new Promise(function dispatchXhrRequest(resolve, reject) {
var requestData = config.data;
var requestHeaders = config.headers;
var responseType = config.responseType;
var onCanceled;
// 请求完成后取消cancelToken订阅事件
function done() {
if (config.cancelToken) {
config.cancelToken.unsubscribe(onCanceled);
}
if (config.signal) {
config.signal.removeEventListener('abort', onCanceled);
}
}
if (utils.isFormData(requestData) && utils.isStandardBrowserEnv()) {
delete requestHeaders['Content-Type']; // Let the browser set it
}
// 创建一个XMLHttpRequest对象 开始发送请求
var request = new XMLHttpRequest();
// HTTP basic authentication
if (config.auth) {
var username = config.auth.username || '';
var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
}
var fullPath = buildFullPath(config.baseURL, config.url);
// 调用open方法
request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
// 设置超时时间
// Set the request timeout in MS
request.timeout = config.timeout;
function onloadend() {
if (!request) {
return;
}
// Prepare the response
var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
var responseData = !responseType || responseType === 'text' || responseType === 'json' ?
request.responseText : request.response;
var response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request
};
settle(function _resolve(value) {
resolve(value);
done();
}, function _reject(err) {
reject(err);
done();
}, response);
// Clean up request
request = null;
}
if ('onloadend' in request) {
// Use onloadend if available
request.onloadend = onloadend;
} else {
// Listen for ready state to emulate onloadend
request.onreadystatechange = function handleLoad() {
if (!request || request.readyState !== 4) {
return;
}
// The request errored out and we didn't get a response, this will be
// handled by onerror instead
// With one exception: request that using file: protocol, most browsers
// will return status as 0 even though it's a successful request
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
return;
}
// readystate handler is calling before onerror or ontimeout handlers,
// so we should call onloadend on the next 'tick'
setTimeout(onloadend);
};
}
// Handle browser request cancellation (as opposed to a manual cancellation)
// 注册取消请求处理函数
request.onabort = function handleAbort() {
if (!request) {
return;
}
reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));
// Clean up request
request = null;
};
// Handle low level network errors
// 注册请求失败处理函数
request.onerror = function handleError() {
// Real errors are hidden from us by the browser
// onerror should only fire if it's a network error
reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request));
// Clean up request
request = null;
};
// Handle timeout
// 注册请求超时处理函数
request.ontimeout = function handleTimeout() {
var timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
var transitional = config.transitional || transitionalDefaults;
if (config.timeoutErrorMessage) {
timeoutErrorMessage = config.timeoutErrorMessage;
}
reject(new AxiosError(
timeoutErrorMessage,
transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
config,
request));
// Clean up request
request = null;
};
// Add xsrf header
// This is only done if running in a standard browser environment.
// Specifically not if we're in a web worker, or react-native.
if (utils.isStandardBrowserEnv()) {
// Add xsrf header
var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
cookies.read(config.xsrfCookieName) :
undefined;
if (xsrfValue) {
requestHeaders[config.xsrfHeaderName] = xsrfValue;
}
}
// Add headers to the request
if ('setRequestHeader' in request) {
utils.forEach(requestHeaders, function setRequestHeader(val, key) {
if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
// Remove Content-Type if data is undefined
delete requestHeaders[key];
} else {
// Otherwise add header to the request
request.setRequestHeader(key, val);
}
});
}
// Add withCredentials to request if needed
if (!utils.isUndefined(config.withCredentials)) {
request.withCredentials = !!config.withCredentials;
}
// Add responseType to request if needed
if (responseType && responseType !== 'json') {
request.responseType = config.responseType;
}
// 监听下载进度
// Handle progress if needed
if (typeof config.onDownloadProgress === 'function') {
request.addEventListener('progress', config.onDownloadProgress);
}
// 监听上传进度
// Not all browsers support upload events
if (typeof config.onUploadProgress === 'function' && request.upload) {
request.upload.addEventListener('progress', config.onUploadProgress);
}
// 取消请求相关 判断如果有cancelToken
if (config.cancelToken || config.signal) {
// Handle cancellation
// eslint-disable-next-line func-names
// 定义一个onCanceled方法 接收一个cancel变量 如果这次请求被取消了 cancel就是取消的reason
onCanceled = function(cancel) {
if (!request) {
return;
}
// 直接reject这次promise reject的原因就是cancel 如果没有cancel 或者cancel.type 则new一个CanceledError
reject(!cancel || cancel.type ? new CanceledError(null, config, req) : cancel);
// 调用abort方法取消XMLHttpRequest请求
request.abort();
request = null;
};
// 调用cancelToken的subscribe方法订阅nCanceled
config.cancelToken && config.cancelToken.subscribe(onCanceled);
if (config.signal) {
config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
}
}
if (!requestData) {
requestData = null;
}
var protocol = parseProtocol(fullPath);
if (protocol && platform.protocols.indexOf(protocol) === -1) {
reject(new AxiosError('Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config));
return;
}
// Send the request
request.send(requestData);
});
};
CancelToken
'use strict';
var CanceledError = require('./CanceledError');
/**
* A `CancelToken` is an object that can be used to request cancellation of an operation.
*
* @param {Function} executor The executor function.
*
* @returns {CancelToken}
*/
function CancelToken(executor) {
// 接收一个executor函数
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
// 定义一个resolvePromise变量
var resolvePromise;
// 初始化一个promise
this.promise = new Promise(function promiseExecutor(resolve) {
// 将该promise的resolve方法赋值给resolvePromise
// 一旦调用resolvePromise this.promise的状态就会被修改为fuifulled
resolvePromise = resolve;
});
// 将this赋值给token
var token = this;
// eslint-disable-next-line func-names
this.promise.then(function(cancel) {
if (!token._listeners) return;
var i = token._listeners.length;
while (i-- > 0) {
token._listeners[i](cancel);
}
token._listeners = null;
});
// eslint-disable-next-line func-names
this.promise.then = function(onfulfilled) {
var _resolve;
// eslint-disable-next-line func-names
var promise = new Promise(function(resolve) {
token.subscribe(resolve);
_resolve = resolve;
}).then(onfulfilled);
promise.cancel = function reject() {
token.unsubscribe(_resolve);
};
return promise;
};
// new CancelToken时会立即调用executor方法 也就是会执行source方法中的cancel = c;
// 这里也就是把cancel函数暴露出去了,把取消的时机留给了使用者 使用者调用cancel时候也就会执行函数内的逻辑
// 一旦调用cancel this.promise的状态就变成了fulfulled
executor(function cancel(message, config, request) {
// 请求已经被取消了直接return
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new CanceledError(message, config, request);
resolvePromise(token.reason);
});
}
/**
* Throws a `CanceledError` if cancellation has been requested.
*/
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};
/**
* Subscribe to the cancel signal
*/
CancelToken.prototype.subscribe = function subscribe(listener) {
if (this.reason) {
listener(this.reason);
return;
}
if (this._listeners) {
this._listeners.push(listener);
} else {
this._listeners = [listener];
}
};
/**
* Unsubscribe from the cancel signal
*/
CancelToken.prototype.unsubscribe = function unsubscribe(listener) {
if (!this._listeners) {
return;
}
var index = this._listeners.indexOf(listener);
if (index !== -1) {
this._listeners.splice(index, 1);
}
};
/**
* Returns an object that contains a new `CancelToken` and a function that, when called,
* cancels the `CancelToken`.
*/
// 返回一个对象 token就是CancelToken实例 实例上有两个属性一个promise一个reason
// cancel就是CancelToken内部传递给外部的cancel函数
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
module.exports = CancelToken;
部分内容参考了lxchuan12.gitee.io/axios 目前只看了这么多,cancelToken只能看个大概,太复杂了╥﹏╥