Axios整体流程
- request(config):将请求拦截器/dispatchRequest()/响应拦截器通过promise链串联起来,返回promise
- dispatch(config):转换请求数据 -> 调用xhrAdapter()发起请求->请求返回后转换响应数据,返回promise
- xhrAdapter(config):创建XHR对象,根据config进行相应配置,发送特定请求,并接收响应数据,返回promise
支持浏览器和NODE两种环境
function getDefaultAdapter() {
var adapter;
if (typeof XMLHttpRequest !== 'undefined') {
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
} else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// For node use HTTP
adapter adapter = require('./adapters/http');
}
return adapter;
}
- xhrAdapter会通过new XMLHttpRequest()生成xhr对象,然后发送请求
- httpAdapter会调用node端内置的http模块发起请求
get/post/put/ 的实质
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);
// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
utils.extend(instance, context);
return instance;
}
- context是Axios实例,上面有defaults( 默认配置)和interceptors(拦截器)两个属性。
- 通过bind函数返回一个新函数,并赋值给instance。所以执行instanc(),其实就是在执行Axios.prototype.request。也就是发送请求的函数。
- 把Axios.prototype的方法都拷贝到instance上
- 把context实例对象上的属性拷贝到instance上
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 }));
};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, data, config) {
return this.request(mergeConfig(config || {}, {
method: method,
url: url,
data: data }));
};
});
将get/post/......等方法通过循环的方式一一挂载到Axios的原型上,而instance已经拷贝了Axios原型上的所有方法。所以可以通过instance.get(config)去发送请求。所有方法的本质最终都是去调用request函数,传入相应的method。
拦截器
- 声明一个chain数组[dispatchRequest,undefined]
- 请求拦截器的回调放到chain前面
- 响应拦截器的回调放到chain后面
拦截器的生成:
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected,
synchronous: options ? options.synchronous : false,
runWhen: options ? options.runWhen : null });
return this.handlers.length - 1; };
use在执行的时候只是把传入的回调保存在了this.handlers中,在request函数中把请求拦截器的回调往数组chain前放,把响应拦截器的回调往数组chain后面放,然后通过promise的then()串连起所有的请求拦截器/请求方法/响应拦截器
// 请求拦截器
var requestInterceptorChain = [];
this.interceptors.request.handlers.forEach(function unshiftRequestInterceptors(interceptor) {
requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected); });
// 响应拦截器
var responseInterceptorChain = [];
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected); });
var promise;
var chain = [dispatchRequest, undefined];
Array.prototype.unshift.apply(chain, requestInterceptorChain);
chain = chain.concat(responseInterceptorChain); promise = Promise.resolve(config);
// 依次执行chain中的所有方法 while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); }
return promise;
取消请求--cancelToken
- 配置cancelToken
- 缓存用于取消请求的函数
- 在特定时间调用cancel函数取消请求
- 本质是调用xhr.abort()方法中断请求
if (config.cancelToken) {
// Handle cancellation config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) { return; }
request.abort();
reject(cancel);
// Clean up
request request = null;
});
}
代码中配置:
let cancel // 用于保存取消请求的函数
function getData() {
axios({
url: '***',
cancelToken: new axios.CancelToken((c) => {
// 保存取消函数,用于之后可能需要取消当前请求
cancel = c })
}).then(res => { const data = res.data }) }
当执行cancel函数时就会执行下面的resolvePromise,改变this.promise为成功的状态,然后xhrAdapter中就会执行config.cancelToken.promise.then执行abort
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}