axios 的拦截器原理
使用拦截器
//添加 请求拦截器
axios.interceptors.request.use((config)=>{
return config
},(error)=>{
return Promise.reject(error);
});
//添加 响应拦截器
axios.interceptors.response.use((response)=> {
return response;
}, (error)=> {
return Promise.reject(error);
});
axios.get('https://xxxx').then(res => {
console.log(res);
})
可以在发起发起请求前,后响应返回后,进行自定义行为,拦截器可以设置多个
原理
下面涉及到的代码多数来自源码,为方便调试,有部分删除
使用 axios时候
//当使用 axios 或 axios.create 实际 就是调用 createInstance()
var axios = createInstance(defaults);
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
createInstance 代码
//实例化 Axios 并 返回 Axios.prototype.request
//故Axios.prototype.request是Axios的核心方法,所有的get、post...都是它的语法糖
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);
return instance;
}
Axios代码内容
// 会实例化 InterceptorManager 是拦截器的核心类
function Axios(instanceConfig) {
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
//Axios 核心 方法 request 中处理拦截器的逻辑如下
//先贴出来,后面介绍其功能
Axios.prototype.request = function request(config) {
// Hook up interceptors middleware
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);//在 [dispatchRequest, undefined] 前面 添加
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);//在 [dispatchRequest, undefined]后面 添加
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
InterceptorManager 代码
'use strict';
//核心类 维护一个数据
function InterceptorManager() {
this.handlers = [];
}
//use => 向数组中添加拦截器逻辑 并 返回 拦截器索引(后面eject会用到)
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
//清除拦截器 通过use返回的id
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
//request 中会有到该方法,是request维护队列的核心方法
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
module.exports = InterceptorManager;
关于拦截器的核心源码如上,先就文章最初的Dome分析,逻辑处理
// 1、当使用 axios 时,Axios被实例化,并内部分别实例化 request、response拦截器
// 2、当使用 use 添加拦截器时,触发 InterceptorManager.prototype.use 方法,将传过来的resove、reject回调,添加在this.handlers[]中
// 3、使用 axios.get('https://xxxx') 时,触发 Axios.prototype.request ,内部触发 InterceptorManager.prototype.forEach
// 3.1 request拦截器 和 response拦截器 触发 Axios.prototype.request 内方法不一样 分别是:this.interceptors.request.forEach、this.interceptors.response.forEach 二者主要区别在于想队列 chain[] 中添加的位置不同,request拦截器从头开始添加 和 response拦截器从尾开始添加,而chain[]本身就含有xhr的 promise 调用返回
// 3.2 从而实现:先触发request拦截器=>发请求,请求返回=>触发response拦截器
// 4 Axios.prototype.request 最后 return promise,就是我们最后使用的axios.get('')这个方法的原样,所有axios.get('').then(res)会再被调用,res就是请求返回的数据被response拦截器处理后的最终结果了
注意下面代码:
// 我们修改最初的Dome,注意传入的回调加上了name:A、B、C、D,方便调试
axios.interceptors.request.use(function A(config) {
console.log(1);
return config
});
axios.interceptors.request.use(function B(config) {
console.log(2);
return config;
});
axios.interceptors.response.use(function C(res) {
console.log(3);
return res
});
axios.interceptors.response.use(function D(res) {
console.log(4);
return res
});
axios.get('https://api.github.com/users/mzabriskie').then(res => {
console.log(5);
})
// 打印结果:2 1 3 4 5
// 因为 Axios.prototype.request 中维护的队列 chain[] 的变化过程如下:
chain[xhr,undefined] => chain[A,undefined,xhr,undefined] => chain[B,undefined,xhr,undefined] => chain[C,undefined,xhr,undefined] => chain[D,undefined,xhr,undefined]
// request拦截器的调用顺序和写入的顺序相反
Axios.js发送请求的核心的逻辑,在 defaults.js中,我们分析一下:
// Axios.js
// 当我们使用 createInstance 传入的 defaults ,就是发送请求的核心
var defaults = require('./defaults');
var axios = createInstance(defaults);
// defaults.js
// 如下 getDefaultAdapter 是 defaults 核心逻辑
var defaults = {
adapter: getDefaultAdapter(),
...
}
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;
}
module.exports = defaults;
//./adapters/xhr.js
// 使用原生 XMLHttpRequest 实现 Ajax
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
var request = new XMLHttpRequest();
request.open(...);
request.timeout = config.timeout;
request.onreadystatechange = function handleLoad() {
...
};
request.onabort = function handleAbort() {
...
};
request.onerror = function handleError() {
...
};
request.ontimeout = function handleTimeout() {
...
};
request.send(requestData);
});
};
思考:最后的Dome中 5 为啥最后打印?
// dispatchRequest 就是答案
var chain = [dispatchRequest, undefined]
//
module.exports = function dispatchRequest(config) {
// adapter 就是 defaults.js 中处理的返回值
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
...
console.log(6)
return response;
}, function onAdapterRejection(reason) {
...
return Promise.reject(reason);
});
};
// chain = [dispatchRequest, undefined]中的dispatchRequestadapter(config).then()
// 如果在 dispatchRequestadapter(config).then() 加一个 console.log(6),如上
// 最后打印结果:2 1 6 3 4 5
// Axios.prototype.request 最后返回的 promise 是chain[]被 while 后的的 promise
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
//while就是按照队列顺序处理所有的拦截器和xhr逻辑,巧妙的利用 promise.then来处理
// 所以最后打印 5 的then(res) 中的res就是 while 后传给这个then的