axios 源码分析 (Axios)

169 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情aaaaaa

核心--构造函数 Axios

在上面的axios.js的文件中

image-20221017152918347

这一步是非常关键的,其中出现了很多次的 Axios。同时这也是 axios的关键所在

Axios构造函数

image-20221017154533973

Axios文件中主要内容在这个Axios构造函数的构建。

里面包括了

  • 调用 axios('url',config),axios({config})的配置方式
  • 默认配置和传入配置的合并
  • 请求方法的设置
  • 请求头配置
  • 拦截器注册
  • 拦截器操作的promise
  • get,post,delete等方法绑定到实例的原型上
  • 暴露处Axios

这些内容,如果把这些内容掌握了axios就差不多了

配置对象config的处理

无论是直接通过axios('url',{config})的方法调用 axios 还是 axios({config}) 或者是 axios.get()的方式使用axios , 都绕不过config的配置

直接调用axios({})或者axios("url",{})

使用这两种方式时,原理上就是直接调用Axios的request方法

这时会对调用时,传入的参数进行判断,

如果第一个参数是字符串形式 即用 axios("url",{})的方法,就会将第一个参数中的 url 复制到 第二个参数的config中

如果第一个字符串不存在 即用 axios({})的方式,就将第一个参数的值,直接给了config,

之后还要将传入配置和axios默认配置进行合并。

通过 axios.get()调用

image-20221018102553981

Axios的原型中绑定了一些列的请求方法,调用这些方法会返回一个 this.request同样也是执行请求,会将调用时传入的一系列参数都合并起来,组成一个 config对象

mergeConfig.js

image-20221018104513586image-20221018104522423

image-20221018104612037

mergeConfig中有争对不同的配置型,确定合并的方式,有一个Map的映射表,还有处理的forEach

将所有的配置文件(用户传入的,默认配置的等等)进行一个合并,最终返回合并后的结果

request()

Axios.js中,Axios构造函数中的 request()时最重要的模块,下面是我对源码详细注释后的 request实力函数

  request(configOrUrl, config) {
    /*eslint no-param-reassign:0*/
    // Allow for axios('example/url'[, config]) a la fetch API
    // 对url的设置,争对调用axios时是否将 url 分出去,做出区分配置
    if (typeof configOrUrl === 'string') {
      config = config || {};
      config.url = configOrUrl;
    } else {
      config = configOrUrl || {};
    }
​
    //将传入的配置和默认配置合并
    // mergeConfig 会将参数中的配置合并在一起,
    config = mergeConfig(this.defaults, config);
​
    const 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);
    }
​
    // Set config.method
    // 确定 config 中的方法名
    config.method = (config.method || this.defaults.method || 'get').toLowerCase();
​
    // 下面的这堆都是对header进行处理,最后生成请求用的headers
    // Flatten headers
    const defaultHeaders = config.headers && utils.merge(
      config.headers.common,
      config.headers[config.method]
    );
​
    defaultHeaders && utils.forEach(
      ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
      function cleanHeaderConfig(method) {
        delete config.headers[method];
      }
    );
​
    config.headers = new AxiosHeaders(config.headers, defaultHeaders);
​
    // filter out skipped interceptors
    const requestInterceptorChain = [];
    // 同步请求拦截器
    let synchronousRequestInterceptors = true;
​
    // 这里的 forEach 不是数组的forEach方法,而是 request 创建的实例 InterceptorManager 中的 实例方法forEach,
    // 遍历的是其中的实例属性 handlers数组。下面的响应拦截器也是一样的
    this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
      // 如果用户没有特殊的进行配置的话这里一般不会执行
      if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
        return;
      }
​
      // interceptor.synchronous 是用户 通过 axios.interceptors.request.use 注册时填入的第三个参数 option中的 synchronous 值,一般默认为false
      // 表示不是同步全球拦截器
      synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
​
      // 在这里将 请求拦截器的内容添加到 chrequestInterceptorChain 中,一组一组的添加
      // 请求拦截器的添加是通过 unshift 添加到 chain 的前面的
      // 由于这种unshift的方法,后注册的请求拦截器会 比 先注册的拦截器的执行快
      requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
    });
​
    const responseInterceptorChain = [];
    // 在这里将 响应拦截器的内容添加到 responseInterceptorChain 中,一组一组的添加
​
    this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
      // 这里也是将 response 中的内容全部添加到 chain 的后面
      responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
    });
​
    let promise;
    let i = 0;
    let len;
​
    // 这里的 synchronousRequestInterceptors 同步请求拦截器 一般指都为 false,然后执行里面的内容
    if (!synchronousRequestInterceptors) {
      // 先把 chain 初始化,第一个是请求,第二个是undefined
      const chain = [dispatchRequest.bind(this), undefined];
      // 然后在前面添加 请求拦截器的 reject和resolve
      chain.unshift.apply(chain, requestInterceptorChain);
      // 然后在后面添加 请求拦截器的 reject和resolve
      chain.push.apply(chain, responseInterceptorChain);
      len = chain.length;
​
      // 创建一个成功的 promise 并把 合并好的config 作为参数传给.then的成功回调
      // 这个config 参数就是 给到所有拦截器的config参数
      promise = Promise.resolve(config);
​
      while (i < len) {
        // 最后再把这些内容 依次调用.then执行,
        // 把 chain中的内容, 一组一组的(拦截器的成功回调和失败回调为一组),添加到.then的 resolve函数和reject函数中
        promise = promise.then(chain[i++], chain[i++]);
      }
      // 最终返回一个这个promise对象
      return promise;
    }
​
    // 从这里往下就是设置的是  synchronousRequestInterceptors 为true时执行的内容
    // 会以同步的方式执行拦截器,而不是 .then 后放入异步队列
    len = requestInterceptorChain.length;
​
    let newConfig = config;
​
    i = 0;
​
    while (i < len) {
      const onFulfilled = requestInterceptorChain[i++];
      const onRejected = requestInterceptorChain[i++];
      try {
        newConfig = onFulfilled(newConfig);
      } catch (error) {
        onRejected.call(this, error);
        break;
      }
    }
​
    ///
    try {
      //dispatchRequest
      promise = dispatchRequest.call(this, newConfig);
    } catch (error) {
      return Promise.reject(error);
    }
​
    i = 0;
    len = responseInterceptorChain.length;
​
    while (i < len) {
      promise = promise.then(responseInterceptorChain[i++], responseInterceptorChain[i++]);
    }
​
    return promise;
  }

我们将如上的内容解读完后,了解到这一步中就是形成了一个 promise链,其中的 dispatchRequest 才是请求的更深层。