• 一比一还原axios源码(六)—— 配置化


      上一章我们完成了拦截器的代码实现,这一章我们来看看配置化是如何实现的。首先,按照惯例我们来看看axios的文档是怎么说的:

        首先我们可以可以通过axios上的defaults属性来配置api。

      我们可以自己创建一个axios实例,传入对应的可配置参数,然后还可以通过defaults来修改。其实就是后写的配置,会覆盖之前的配置。那么接下来我们就来看代码吧~

      首先,我们在lib根目录下,创建一个defaults文件,在里面写下我们的默认配置:

      目前来说吼,就这么几个配置。 adapter这个默认配置,是用来区分宿主环境的也就是是使用xhr还是http,在咱们这里getDefaultAdapter是这样的:

    复制代码
    
    
     import adapterXHR from "./adapters/xhr";

    function
    getDefaultAdapter() { var adapter; if (typeof XMLHttpRequest !== "undefined") { // For browsers use XHR adapter // adapter = require("./adapters/xhr"); adapter = adapterXHR; } else if ( typeof process !== "undefined" && Object.prototype.toString.call(process) === "[object process]" ) { // For node use HTTP adapter // 咱们没有实现服务端的http,所以直接用xhr的 adapter = adapterXHR; // adapter = require("./adapters/http"); } return adapter; }
    复制代码

      很简单哈,就是判断下宿主环境,因为我这里没有实现http部分,所以两个条件判断引入的都是同一个xhr文件。我们再往下看就是默认的transformRequest方法:

    复制代码
      transformRequest: [
        function transformRequest(data, headers) {
          normalizeHeaderName(headers, "Accept");
          normalizeHeaderName(headers, "Content-Type");
    
          if (
            utils.isFormData(data) ||
            utils.isArrayBuffer(data) ||
            utils.isBuffer(data) ||
            utils.isStream(data) ||
            utils.isFile(data) ||
            utils.isBlob(data)
          ) {
            return data;
          }
          if (utils.isArrayBufferView(data)) {
            return data.buffer;
          }
          if (utils.isURLSearchParams(data)) {
            setContentTypeIfUnset(
              headers,
              "application/x-www-form-urlencoded;charset=utf-8"
            );
            return data.toString();
          }
    
          var isObjectPayload = utils.isObject(data);
          var contentType = headers && headers["Content-Type"];
    
          if (isObjectPayload && contentType === "multipart/form-data") {
            return toFormData(
              data,
              new ((this.env && this.env.FormData) || FormData)()
            );
          } else if (isObjectPayload || contentType === "application/json") {
            setContentTypeIfUnset(headers, "application/json");
            return stringifySafely(data);
          }
    
          return data;
        },
      ],
    复制代码

      这个transformRequest做的事情比较多,首先就是调用normalizeHeaderName转换两个请求头,这个之前说过,就不多说了,再然后其实就是判断请求body的类型,然后进行一定的请求头适应和转换。transformResponse则比较简单:

    复制代码
      transformResponse: [
        function transformResponse(data) {
          if (utils.isString(data) && data.length) {
            try {
              return JSON.parse(data);
            } catch (e) {
              if (e.name === "SyntaxError") {
                throw enhanceError(e, this, "E_JSON_PARSE");
              }
              throw e;
            }
          }
          return data;
        },
      ],
    复制代码

      这里我去掉了axios原有的transitional参数,因为这个东西可以说几乎用不太到这么细,有兴趣的话大家可以自己去看,如果你一步一步跟到了这里,那么看懂这个参数的应用肯定不在话下。

    再然后就是timeout、headers和validateStatus:

    复制代码
      timeout: 0,
    
      validateStatus: function validateStatus(status) {
        return status >= 200 && status < 300;
      },
    
      headers: {
        common: {
          Accept: "application/json, text/plain, */*",
        },
      },
    复制代码

      timeout和headers不说了,validateStatus实际上就是promise走reject的条件。OK,最后我们还要给defaults的headers根据不同的方法,加一下请求头字段:

    复制代码
    utils.forEach(["delete", "get", "head"], function forEachMethodNoData(method) {
      defaults.headers[method] = {};
    });
    
    utils.forEach(["post", "put", "patch"], function forEachMethodWithData(method) {
      defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
    });
    复制代码

      很简单,不多说。完整的代码就在这里,大家可以去看。下面我们就要来看下如何把defaults配置融合进我们的请求中去。首先,我们要找到创建axios的源头的类,也就是Axios类,我们加一点代码:

    复制代码
    export default function Axios(config) {
      this.defaults = config;
      this.interceptors = {
        request: new InterceptorManager(),
        response: new InterceptorManager(),
      };
    }
    复制代码

      多了个this.defaults,没错,我们会在创建实例的时候,传入我们上面编写好的defaults,那么我们就去axios.js里面,修改下代码吧:

    var axios = createInstance(defaults);

      然后,我们给createInstance方法加一点东西:

    instance.create = function create(instanceConfig) {
        return createInstance(mergeConfig(defaultConfig, instanceConfig));
      };

      也就是我们给生成的Axios实例instance提供一个创建实例的方法create。OK,到了这里哈,我们就可以使用这个defaults配置了,但是我们要去哪用呢?没错!就是Axios的prototype上挂载的request方法里。到目前为止,咱们稍微的小小的回顾下:首先我创建了defaults默认配置 ---> 然后我在Axios类里接收配置 ---> 最后,我在创建axios实例的时候把默认配置传入到Axios类里。那么我们继续,在request方法内,我们需要把创建实例时默认配置和axios实例使用时传入的配置合并一下,注意!我们之前所说的所有的“配置”都是指默认配置,默认配置是在创建实例的时候传递给Axios类使用的。

      var context = new Axios(defaultConfig);

      我们合并的配置是合并的默认配置和手动配置,手动配置指的是传递给axios实例的配置:

    复制代码
    axios({
      url: "/c6/post",
      method: "post",
      data: qs.stringify({
        a: 1,
      }),
      headers: {
        test: "321",
      },
    }).then((res) => {
      console.log(res.data);
    });
    复制代码

      比如这个axios里传的这个对象。我们言归正传,合并下配置:

      config = mergeConfig(this.defaults, config);

      就可以了。这个mergeConfig,之前有说过,不多说了吼。到了现在还没完,我们还有个核心的地方,就是adapter,也就是说,我们要在哪使用这个adapter,怎么使用。之前的dispatchRequest很简单,但是这里我们需要多做一些额外的处理:

    复制代码
    import defaults from "../defaults";
    import utils from "../utils";
    import transformData from "./transformData";
    
    /**
     * Dispatch a request to the server using the configured adapter.
     *
     * @param {object} config The config that is to be used for the request
     * @returns {Promise} The Promise to be fulfilled
     */
    export default function dispatchRequest(config) {
      // Ensure headers exist
      config.headers = config.headers || {};
    
      // Transform request data
      config.data = transformData.call(
        config,
        config.data,
        config.headers,
        config.transformRequest
      );
    
      // Flatten headers
      config.headers = utils.merge(
        config.headers.common || {},
        config.headers[config.method] || {},
        config.headers
      );
    
      utils.forEach(
        ["delete", "get", "head", "post", "put", "patch", "common"],
        function cleanHeaderConfig(method) {
          delete config.headers[method];
        }
      );
    
      var adapter = config.adapter || defaults.adapter;
    
      return adapter(config).then(
        function onAdapterResolution(response) {
          // Transform response data
          response.data = transformData.call(
            config,
            response.data,
            response.headers,
            config.transformResponse
          );
    
          return response;
        },
        function onAdapterRejection(reason) {
          return Promise.reject(reason);
        }
      );
    }
    复制代码

      首先哈,我们获取一下config的headers,然后转换一下config.data,再然后,还记不记得,defaults里的headers是一个二维深度的对象,那么我们需要把它降维,最后我们获取adapter,返回这个adapter的promise即可。到了这里,我粗略的带大家过完了配置化的整条线。其中我略过了一些不常用的源码,也有一部分工具方法没有深入的去讲,那些我个人觉得大家可以自己去看,再读文章的时候,一定要对比着源码来思考,不然的话,可能不太容易理解我说的是啥。

      这章到这里就完事啦。下一章我们去实现axios的取消功能。

  • 相关阅读:
    C#中async/await的线程ID变化情况
    Nginx:负载均衡(策略讲解+配置举例)
    BeginCTF 2024(自由赛道)MISC
    股票多因子模型之截面回归
    一文读懂二级分销返利模式,商城系统源码机制分享
    什么是分支和合并(阁瑞钛伦特软件-九耶实训)
    壹基金防灾减灾宣传进社区 提升家庭安全能力
    计算机毕业设计(83)php小程序毕设作品之服务预约家政小程序系统
    Falcon 登陆 Hugging Face 生态
    Towards a General Purpose CNN for Long Range Dependencies in ND
  • 原文地址:https://www.cnblogs.com/zaking/p/15956619.html