defaults The default config for the instance

defaults 初始化实例的默认配置的库函数

# 一、环境准备

git clone https://github.com/axios/axios.git

cd axios

npm start

http://localhost:3000/

# 二、函数研读

# 1. helper 函数

包含辅助函数库 utils 、非通用的函数库normalizeHeaderName及核心函数库enhanceError

# 【1.1】 utils

utils是一个非特定于 axios 的通用辅助函数库。见名思义,其中包含了一些与业务逻辑无关但或可以提升效率或频繁使用到的代码段,谓之工具函数。关于 axios 的工具函数库 utils 更多内容,详情见utils

# 【1.2】 normalizeHeaderName

var normalizeHeaderName = require('./helpers/normalizeHeaderName');

===>
'use strict';

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

module.exports = function normalizeHeaderName(headers, normalizedName) {
  utils.forEach(headers, function processHeader(value, name) {
    if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
      headers[normalizedName] = value;
      delete headers[name];
    }
  });
};

  • 作用是规范化请求头Header中的名,替换不满足Axios内部要求的
  • 通过toUpperCase()将用户自定义Header中键名转换成大写并与 Axios 内部默认配置的对应大写形式比较
  • forEach循环键值对headers时,每次都会回调processHeader,入参keyvalue对应headers中每一项的键值

Tips: 这里用到了utils中的forEach函数,每次调用normalizeHeaderName会对headers所有做一次检查

# 【1.3】 enhanceError

var enhanceError = require('./core/enhanceError');

===>
'use strict';

/**
 * Update an Error with the specified config, error code, and response.
 *
 * @param {Error} error The error to update.
 * @param {Object} config The config.
 * @param {string} [code] The error code (for example, 'ECONNABORTED').
 * @param {Object} [request] The request.
 * @param {Object} [response] The response.
 * @returns {Error} The error.
 */
module.exports = function enhanceError(error, config, code, request, response) {
  error.config = config;
  if (code) {
    error.code = code;
  }

  error.request = request;
  error.response = response;
  error.isAxiosError = true;

  error.toJSON = function toJSON() {
    return {
      // Standard
      message: this.message,
      name: this.name,
      // Microsoft
      description: this.description,
      number: this.number,
      // Mozilla
      fileName: this.fileName,
      lineNumber: this.lineNumber,
      columnNumber: this.columnNumber,
      stack: this.stack,
      // Axios
      config: this.config,
      code: this.code,
      status: this.response && this.response.status ? this.response.status : null
    };
  };
  return error;
};

# 2. 正文

# 【2.1】常量

var DEFAULT_CONTENT_TYPE = {
    "Content-Type": "application/x-www-form-urlencoded",
};

avatar

  • 由于http协议规定网页传输内容以文本形式传递,因此需要约定传输内容body的格式
  • Content-Type是一个必要的字段,需要有默认值,不同的Content-Type影响body的格式
  • Cntent-Type默认被指定为application/x-www-form-urlencoded时提交的数据按照 key1=val1&key2=val2 的方式进行编码,具有良好的兼容性

# 【2.2】内部函数 setContentTypeIfUnset

function setContentTypeIfUnset(headers, value) {
    if (
        !utils.isUndefined(headers) &&
        utils.isUndefined(headers["Content-Type"])
    ) {
        headers["Content-Type"] = value;
    }
}
  • 使用到了utils中的isUndefined方法判定数据是否定义
  • 请求头headers有定义但其中Content-Type未定义,需要设定初值val

# 【2.3】内部函数 getDefaultAdapter

function getDefaultAdapter() {
    var adapter;
    if (typeof XMLHttpRequest !== "undefined") {
        // For browsers use XHR adapter
        adapter = require("./adapters/xhr");
    } else if (
        typeof process !== "undefined" &&
        Object.prototype.toString.call(process) === "[object process]"
    ) {
        // For node use HTTP adapter
        adapter = require("./adapters/http");
    }
    return adapter;
}
  • 根据不同宿主环境[Browser、node]获取适配器,可以理解为创建了一个用于与服务器交互的实例对象
  • 关于适配器的更多内容可以在MDN (opens new window)上看一下浏览器(Browser)端的xhr(XMLHttpRequest)

Tips: 不用过多的纠结适配器的概念,后面的文章会深入分析[Browser、node]的适配器

# 【2.4】内部函数 stringifySafely

function stringifySafely(rawValue, parser, encoder) {
    if (utils.isString(rawValue)) {
        try {
            (parser || JSON.parse)(rawValue);
            return utils.trim(rawValue);
        } catch (e) {
            if (e.name !== "SyntaxError") {
                throw e;
            }
        }
    }

    return (encoder || JSON.stringify)(rawValue);
}
  • 严格化rawValue,说白了就是去除首位空格
  • 使用自定义parserJSON.parse解析 JSON 字符串,转化成对应的对象
  • 若字符串不符合 JSON 规范,其中try {} catch()会捕获并会抛出 SyntaxError 异常,否则调用utils中的trim函数去除首位空格
  • 返回时使用自定义encoderJSON.stringify再将rawValue转换成Json字符串

Tips: 目前为止源码没有出现自定义的parserencoder🐶

# 【2.5】构造对象 defaults

这里到了默认配置的主体

var defaults = {
    transitional: {
        silentJSONParsing: true,
        forcedJSONParsing: true,
        clarifyTimeoutError: false,
    },

    adapter: getDefaultAdapter(),

    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();
            }
            if (
                utils.isObject(data) ||
                (headers && headers["Content-Type"] === "application/json")
            ) {
                setContentTypeIfUnset(headers, "application/json");
                return stringifySafely(data);
            }
            return data;
        },
    ],

    transformResponse: [
        function transformResponse(data) {
            var transitional = this.transitional || defaults.transitional;
            var silentJSONParsing =
                transitional && transitional.silentJSONParsing;
            var forcedJSONParsing =
                transitional && transitional.forcedJSONParsing;
            var strictJSONParsing =
                !silentJSONParsing && this.responseType === "json";

            if (
                strictJSONParsing ||
                (forcedJSONParsing && utils.isString(data) && data.length)
            ) {
                try {
                    return JSON.parse(data);
                } catch (e) {
                    if (strictJSONParsing) {
                        if (e.name === "SyntaxError") {
                            throw enhanceError(e, this, "E_JSON_PARSE");
                        }
                        throw e;
                    }
                }
            }

            return data;
        },
    ],

    /**
     * A timeout in milliseconds to abort a request. If set to 0 (default) a
     * timeout is not created.
     */
    timeout: 0,

    xsrfCookieName: "XSRF-TOKEN",
    xsrfHeaderName: "X-XSRF-TOKEN",

    maxContentLength: -1,
    maxBodyLength: -1,

    validateStatus: function validateStatus(status) {
        return status >= 200 && status < 300;
    },

    headers: {
        common: {
            Accept: "application/json, text/plain, */*",
        },
    },
};
  • 这里对请求头header和传输内容data作了一些处理,此外还做了一些配置的初始化
  • transitional包含了一些过度属性配置项,这些项对开发者不可见,主要是为了兼容之前的版本,经过配置可在较新版本中删除向后兼容的某些性质
  • transformResponse在向服务器发送请求前对数据作一些修改,会对请求内容data作一些格式化操作,转换成服务器更易于接收的格式,可以搭配utils函数库分析
  • transformResponse主要是根据transitional配置对返回内容作一些格式上的调整,并在出错时对错误信息作一些补充说明
配置项 作用
[transitional-silentJSONParsing] 版本兼容配置-返回值转换为 Json 出错时是否置为 null 返回
[transitional-forcedJSONParsing] 版本兼容配置-responseType 设置非 json 类型时是否强制转换成 json 格式
[transitional-clarifyTimeoutError] 版本兼容配置-请求超时时是否默认返回 ETIMEDOUT 类型错
adapter 选择默认的适配器,需要注意的是这里允许自定义处理请求
transformRequest 允许在向服务器发送前,修改请求数据
transformResponse 在传递给 then/catch 前,允许修改响应数据
timeout 指定请求超时的毫秒数(0 表示无超时时间)
xsrfCookieName 用作 xsrf token 的值的 cookie 的名称
xsrfHeaderName 承载 xsrf 令牌值的 http 头的名称
maxContentLength 定义允许的响应内容的最大尺寸
maxBodyLength 默认 body 最大长度限制
validateStatus 定义对于给定的 HTTP 响应状态码是 resolve 或 reject 或 promise
[headers-common-Accept] "object"

Tips: 关于配置更多的说明,详情见axios 请求配置 (opens new window),以及axios Readme (opens new window)

# 【2.6】内部函数 stringifySafely

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);
});
  • 这里对几种请求初始化请求头,[delete, get, head]三种不需要提交表单内容的请求头header为空,[post, put, patch]三种需要提交表单内容的请求头header设定Content-Typeapplication/x-www-form-urlencoded,很好理解,不做过多赘述

# 三、参考

1. 的文章工具函数 utils 研读解析 (opens new window)

2. 李冰老师的专栏图解 Google V8 - 函数调用是如何影响到内存布局的 (opens new window)

3. winter老师的前端训练营 - 我总结的笔记 (opens new window)

4. MDN (opens new window)