从零实现axios(2.3小节-应用配置项)

32 阅读3分钟

处理配置选项

我们定义好了默认配置对象,并且把它们跟用户自定义的配置对象合并在了一起,现在是时候使用这些配置项了。

我们先在dispatchRequest.js中的 dispatchRequest 函数来处理、使用这些配置项 这里面处理的事情非常简单,就是把 headers 里面嵌套的对象,扁平化到 headers 上,减少属性的查找层级

module.exports = function dispatchRequest(config) {
  // 确保 headers 存在
  config.headers = config.headers || {};

  // 压平headers
  config.headers = utils.merge(
    config.headers.common || {},
    config.headers[config.method] || {},
    config.headers
  );

  // 删除headers的以下属性,因为这些属性值为对象,且对象里面的内容已经被压平到headers中去了
  utils.forEach(
    ["delete", "get", "head", "post", "put", "patch", "common"],
    function cleanHeaderConfig(method) {
      delete config.headers[method];
    }
  );

  // 以下部分代码请参考前面的章节或最后一章提供的参考资料
  // ....
};

我们接着在xhr.js中的 xhrAdapter 函数来处理、使用这些配置项。 首先我们通过 buildFullPath 函数把 baseURL,跟 url 构建成一个完整的 url 路径。然后通过 buildURL 函数把 config 对象上的 params 对象参数序列化到 url 中。

module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    // 请求数据
    var requestData = config.data;

    // 构造一个xhr对象
    var request = new XMLHttpRequest();

    // 构建全路径
    var fullPath = buildFullPath(config.baseURL, config.url);

    // xhrReq.open(method, url, async)
    // method: HTTP方法,比如GET、POST
    // url: 请求的URL
    // async: 表示是否异步执行操作,默认为true
    request.open(
      config.method.toUpperCase(),
      buildURL(fullPath, config.params, config.paramsSerializer),
      true
    );
  });
};

我们在 core 文件下创建 buildFullPath.js 文件,buildFullPath 函数接收两个参数,一个是 baseURL,一个是 requestedURL, 我们用 isAbsoluteURL 函数来对 requestedURL 参数作判断,如果该 url 不是绝对路径,则用 combineURLs 函数把 baseURL 跟其组合成新的 url

"use strict";

var isAbsoluteURL = require("../helpers/isAbsoluteURL");
var combineURLs = require("../helpers/combineURLs");

/**
 * 如果requestedURL不是绝对路径,则组合baseURL和requestedURL来创建新的URL
 * 如果是绝对url则直接返回
 * @param {string} baseURL 基础url
 * @param {string} requestedURL 请求url
 * @returns {string} 组合后的完整路径
 */
module.exports = function buildFullPath(baseURL, requestedURL) {
  if (baseURL && !isAbsoluteURL(requestedURL)) {
    return combineURLs(baseURL, requestedURL);
  }
  return requestedURL;
};

我们在 helpers 文件夹下创建 isAbsoluteURL.js 文件和 combineURLs.js 文件,分别实现isAbsoluteURL 函数和 combineURLs 函数

"use strict";

/**
 * 测试url是否为绝对url
 *
 * @param {string} url 要测试的url
 * @returns {boolean}
 */
module.exports = function isAbsoluteURL(url) {
  // 如果url以 "<scheme>://" or "//" 开头,则认为是绝对url
  return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
};
"use strict";

/**
 * 组合成新的url
 *
 * @param {string} baseURL 基础url
 * @param {string} relativeURL 相对URL
 * @returns {string}
 */
module.exports = function combineURLs(baseURL, relativeURL) {
  return relativeURL
    ? baseURL.replace(/\/+$/, "") + "/" + relativeURL.replace(/^\/+/, "")
    : baseURL;
};

我们在 helpers 文件夹下创建 buildURL.js 文件,来实现 buildURL 函数。该函数做的事情非常简单,就是把 config.params 序列化到 url 上,代码如下

var utils = require("../utils");

// 对参数键值进行编码
function encode(val) {
  return encodeURIComponent(val)
    .replace(/%3A/gi, ":")
    .replace(/%24/g, "$")
    .replace(/%2C/gi, ",")
    .replace(/%20/g, "+")
    .replace(/%5B/gi, "[")
    .replace(/%5D/gi, "]");
}

/**
 * 序列化params参数到url中
 *
 * @param {string} url (e.g., http://www.google.com)
 * @param {object} [params] 要序列化的params参数
 * @returns {string} 格式化后的参数
 */
module.exports = function buildURL(url, params, paramsSerializer) {
  /*eslint no-param-reassign:0*/
  if (!params) {
    return url;
  }

  var serializedParams;
  if (paramsSerializer) {
    // 如果开发者自定义了params的序列化参数函数,则使用自定义的函数
    serializedParams = paramsSerializer(params);
  } else if (utils.isURLSearchParams(params)) {
    // 如果params是URLSearchParams类型
    serializedParams = params.toString();
  } else {
    var parts = [];

    utils.forEach(params, function serialize(val, key) {
      // 值不存在,则忽略该键
      if (val === null || typeof val === "undefined") {
        return;
      }

      if (utils.isArray(val)) {
        key = key + "[]";
      } else {
        val = [val];
      }

      utils.forEach(val, function parseValue(v) {
        if (utils.isDate(v)) {
          // 值是日期类型,则转换为ISOString
          v = v.toISOString();
        } else if (utils.isObject(v)) {
          // 值是对象类型,则json化
          v = JSON.stringify(v);
        }
        parts.push(encode(key) + "=" + encode(v));
      });
    });

    serializedParams = parts.join("&");
  }

  if (serializedParams) {
    var hashmarkIndex = url.indexOf("#");
    // 去掉哈希
    if (hashmarkIndex !== -1) {
      url = url.slice(0, hashmarkIndex);
    }

    // url存在'?'则直接拼接上参数,否则在url后添加'?', 再拼接
    url += (url.indexOf("?") === -1 ? "?" : "&") + serializedParams;
  }

  return url;
};

buildURL函数用到了一些 utils.js 中的辅助函数,我们来实现这些函数

function kindOfTest(type) {
  type = type.toLowerCase();
  return function isKindOf(thing) {
    return kindOf(thing) === type;
  };
}

function isObject(val) {
  return val !== null && typeof val === "object";
}

var isDate = kindOfTest("Date");

var isURLSearchParams = kindOfTest("URLSearchParams");

module.exports = {
  isArray,
  forEach: forEach,
  merge: merge,
  trim: trim,
  extend: extend,
  isObject: isObject,
  isPlainObject: isPlainObject,
  isUndefined: isUndefined,
  isDate: isDate,
  isURLSearchParams: isURLSearchParams,
};