在构建现代前端应用时,网络请求处理是关键环节。作为最流行的HTTP客户端库之一,Axios通过其拦截器机制(Interceptors)提供了强大的请求/响应处理能力。本文将深入Axios源码,揭示拦截器背后的精妙设计与实现原理。
在深入源码前,先理解拦截器的核心作用:
// 典型拦截器使用示例
axios.interceptors.request.use(config => {
config.headers.Authorization = `Bearer ${token}`;
return config;
});
axios.interceptors.response.use(
response => response.data,
error => handleApiError(error)
);
我们从Axios核心源码(lib/core/Axios.js)切入分析:
// lib/core/InterceptorManager.js
function InterceptorManager() {
this.handlers = [];
}
InterceptorManager.prototype.use = function(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1; // 返回ID用于eject
};
InterceptorManager.prototype.eject = function(id) {
if (this.handlers[id]) {
this.handlers[id] = null; // 置空而非删除保持索引稳定
}
};
关键设计:
// lib/core/Axios.js
Axios.prototype.request = function(config) {
// 初始化执行链
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
// 插入请求拦截器
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 插入响应拦截器
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
// 构建Promise链
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
执行链构建流程:
[请求拦截器3, 请求拦截器2, 请求拦截器1, dispatchRequest, 响应拦截器1, 响应拦截器2]
关键代码解析:
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
执行特点:
Axios拦截器融合了多种经典设计模式:
与Koa中间件高度相似:
// Koa中间件 VS Axios拦截器
app.use(async (ctx, next) => {
// 请求处理
await next();
// 响应处理
});
axios.interceptors.request.use(config => {
// 请求处理
return config; // 相当于next()
});
dispatchRequest
作为核心适配器:
// lib/core/dispatchRequest.js
module.exports = function dispatchRequest(config) {
// 选择浏览器/XHR环境适配器
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(...);
};
axios.interceptors.request.use(async config => {
const token = await refreshToken();
config.headers.Authorization = token;
return config;
});
axios.interceptors.request.use(config => {
if (shouldBlock(config)) {
return Promise.reject(new Error('Request blocked'));
// 或创建CancelToken取消请求
}
return config;
});
axios.interceptors.response.use(null, error => {
const config = error.config;
if (!config || !config.retry) return Promise.reject(error);
config.__retryCount = config.__retryCount || 0;
if (config.__retryCount >= config.retry) {
return Promise.reject(error);
}
config.__retryCount++;
return new Promise(resolve => {
setTimeout(() => resolve(axios(config)), config.retryDelay || 1000);
});
});
axios.interceptors.request.use(config => {
// CSRF防护
config.xsrfCookieName = 'XSRF-TOKEN';
config.xsrfHeaderName = 'X-XSRF-TOKEN';
// HTTP参数污染防护
config.paramsSerializer = params =>
qs.stringify(params, { arrayFormat: 'brackets' });
return config;
});
在Chrome DevTools中定位:
node_modules/axios/lib/core/Axios.js
-> request 方法
-> while 循环
const debugInterceptor = {
request: {
apply: (target, thisArg, args) => {
console.log('[Request Interceptor]', args[0]);
return target.apply(thisArg, args);
}
}
};
axios.interceptors.request.use(
new Proxy(interceptorFn, debugInterceptor)
);
axios.interceptors.request.use(config => {
config.metadata = { startTime: performance.now() };
return config;
});
axios.interceptors.response.use(response => {
const duration = performance.now() - response.config.metadata.startTime;
console.log(`Request took ${duration.toFixed(2)}ms`);
return response;
});
Axios拦截器的精妙之处在于:
这种设计使Axios在保持核心简洁的同时,具备了强大的扩展能力。理解其实现原理不仅能提升调试效率,更能启发我们设计出更优雅的异步处理系统。在微前端架构、BFF层等现代前端场景中,这种管道式处理思想具有广泛的应用价值。
源码探索的最佳实践:在node_modules中放置axios源码副本,配合断点调试逐步跟踪执行流。真正的理解来自亲手拆解与重建的过程。