axios源码学习(三):axios怎么实现拦截器?

3,603 阅读3分钟

axios怎么实现拦截器?为什么要实现拦截器?

拦截器的作用

我们现在说说这个问题。

axios的拦截器作用非常大。axios的拦截器分为请求拦截器跟响应拦截器,都是可以设置多个请求或者响应拦截。每个拦截器都可以设置两个拦截函数,一个为成功拦截,一个为失败拦截。在调用axios.request()之后,请求的配置会先进入请求拦截器中,正常可以一直执行成功拦截函数,如果有异常会进入失败拦截函数,并不会发起请求;调起请求响应返回后,会根据响应信息进入响应成功拦截函数或者响应失败拦截函数。

因此,我们可以在拦截器中处理一些请求的统一处理。比如在请求拦截器中设置请求头,处理统一的请求数据,在响应拦截去中根据响应状态码做统一的提示信息,整理响应数据等。

// 官方用例
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });

如果有一些请求有特有的拦截需求,还可以添加后再删除:

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

拦截器的源码实现

关于拦截器的源码实现,其实在第一篇的源码分析中已经涉及到主要的部分了。

我们可以观察一下拦截器的写法,跟Promise.then的写法是很像的。说明内部的实现也会跟手写promise的实现差不多。事实上源码的实现多少也有点那个味。

axios中的拦截器是一个类,然后会在创建Axios实例时创建两个拦截器实例,一个作为请求拦截,一个作为相应拦截。

// 拦截器源码
function InterceptorManager() {
  // 通过axios.interceptors.use来设置拦截器,然后推入handlers中,然后就可以设置多个拦截器
  this.handlers = [];
}


// Axios源码
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = { // 拦截器
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

然后当用户调axios.interceptors.[request/response].use()(其实这个名字挺长的,作者也没有在Axios下设置别名,估计觉得这样写语义更强吧)时,就是调拦截器的use方法。这个方法会把传入的两个函数进行组合入栈this.handlers,然后返回对应的下标。

// InterceptorManager源码
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1; // 返回当前拦截器的index,作为拦截器的id
};

当用户调用axios.interceptors.request.eject()时,会拿传入的id去把栈this.handlers中对应下标的内容置空。

// InterceptorManager源码
InterceptorManager.prototype.eject = function eject(id) {
  // id是在use注册拦截器的时候返回
  // 这里根据拦截器的id来删除
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};

当用户调用axios.request()时,内部会调用请求跟响应拦截器的InterceptorManager.prototype.forEach拿到每一组拦截函数,然后推入数组chain中。

// Axios源码
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);
});

实际到之后,在调用拦截函数时,已经不是在拦截器实例中调用了,所以拦截器的作用更多是一个存储栈的作用,实现的3个api也是在做栈的处理。所以不如说InterceptorManager是拦截器存储栈类(类名直译也是拦截器管理的意思)。

以上就是axios拦截器的实现分析。下一篇会整理一下axios整个处理流程。