The introduction

Familiar with Vue students, must have used Axios, concise API syntax and convenient interceptor have been warmly welcomed by developers, behind the use, whether also want to understand the implementation principle, in fact, the source code is not difficult to read, here I will throw out a brick to introduce jade, to do a brief analysis of the source code

This article is intended for developers who use Axios a lot, so the following content assumes that the reader is familiar with or proficient in Axios and does not provide a detailed description of Axios functionality

This article, the first in a series, examines the Axios export instance and the default configuration

The Axios version analyzed in this article is 0.21.0

review

Let’s start by reviewing the common Axios features

  • axios(config) / axios.get / axios.post / axios.delete ….

    Call AXIos, pass in a request, or make a get, POST, delete request directly

  • axios.create

    Create a new axios instance

  • axios.defaults.xxxx

    Configure the default configuration of AXIOS

  • axios.interceptors.request / axios.interceptors.response

    Configure global interceptors for AXIOS, or you can configure interceptors only for an instance created through CREATE

  • cancel

    Cancel a request

Next we according to the source code one by one analysis, the above function is how to achieve

The code structure

The main directory in the source code is lib, and the rest are unit tests, documentation, etc., which I won’t go into here

Lib │ axios.js │ defaults. Js │ ├─ Adapters │ HTTP.js │ Readme.md │ XHR. Js │ ├─ Cancel │ Cancel Canceltok.js │ isCancel. Js │ ├─core │ axios.js │ buildFullPath.js │ createError.js │ dispatchRequest.js │ EnhanceError. Js │ interceptormanager.js │ mergeConfig. Js │ readme. md │ settle buildURL.js combineURLs.js cookies.js deprecatedMethod.js isAbsoluteURL.js isAxiosError.js isURLSameOrigin.js normalizeHeaderName.js parseHeaders.js README.md spread.jsCopy the code

There are several important files (axios.js, defaults.js, utils.js) and several classes of files in four separate folders

  • axios.js

    This file serves as an entry file for generating and exporting AXIos objects, as well as extending a series of functional methods such as axios.create

  • defaults.js

    This file is the default configuration

  • utils.js

    This file is a series of helper methods, such as isStirng, extend, and so on

  • adapter

    This directory is where the adaptors for Ajax, namely HTTP libraries that encapsulate native xmlHttpRequest or Node, are processed into their own methods for easy use. If there are newer Ajax methods, such as FETCH, Axios just needs to modify this part to fit the new interface

  • cancel

    This implements the function of canceling the request

  • core

    This is the core code of Axios, including Axios base class, some error wrapper, etc. The core code is axios.js, dispatchRequest.js, interceptorManager.js

  • helpers

    The other helper functional modules here are probably different from utils.js because utils.js is a more general method, while the helper methods in this directory are custom

The Adapter directory is useful because it uses the adapter pattern in design pattern, which can be read further if you are interested

Source code analysis

Axios (config)/axios.get/axios.create and other methods

Instead of focusing on the implementation, let’s see how to expose abstract methods for developers to use, focusing on lib/axios.js

/ / path: lib/axios. Js

var utils = require('./utils');
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');

function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);
  var instance = bind(Axios.prototype.request, context);

  // Copy axios.prototype to instance
  utils.extend(instance, Axios.prototype, context);

  // Copy context to instance
  utils.extend(instance, context);

  return instance;
}

// Create the default instance to be exported
var axios = createInstance(defaults);

// Factory for creating new instances
axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};

axios.Cancel = ...
axios.CancelToken = ...
axios.isCancel = ...
axios.all = ...
axios.spread = ...
axios.isAxiosError = ...

module.exports = axios;
Copy the code

As you can see, the axios that our developers are using is also an instance of the axios base class (not a pure instance, strictly speaking).

If you want to export an instance of axios(config)/axios.get/axios.create, you can export an instance of axios(config)/axios.get/axios.create To extend this instance to make it more powerful, let’s take a step-by-step look at how this is done:

  • var context = new Axios(defaultConfig);
    var instance = bind(Axios.prototype.request, context);
    Copy the code

    These two lines generate an instance context, bind this, the context of the request method of the base class Axios, to the instance context, and generate a new method instance, which will be the Axios object we’ll use in the future, At this point, the AXIos object is essentially just a Request method

    Bind is the es6 equivalent of the function.prototype. bind method, except axios implements it itself here for compatibility

  • utils.extend(instance, Axios.prototype, context);
    Copy the code

    In this step, extend merges axios.prototype’s prototype chain into instance (Request), which is redundant because instance’s prototype also contains its request method. Here we implement axios(config) (equivalent to axios.request(config)) and axios.get/axios.post… (Prototype chain method) these several functions

    Prototype extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends

  • utils.extend(instance, context)
    Copy the code

    This step, again using the extend method, merges other properties of the context, such as defaults and Interceptors, into the instance object

  • axios.create = function create(instanceConfig) {
      return createInstance(mergeConfig(axios.defaults, instanceConfig));
    };
    Copy the code

    This extends the createInstance function to axios.create, allowing developers to create new instances, as well as the Cancel, all, and other methods

Finally, we have an objectaxiosIt looks likeAxiosAn instance of the class, which also acts like an instance, is just onerequestMethod, but with instance properties and stereotypes, and some other apis

When I was younger, I took it for granted that an instance created by axios.create could be subclassed by create. Now I read the source code and realized that there was no such design. I was at the fifth layer and the author was at the second.

The default configuration of AXIos

Before diving into the logic, let’s take a look at the default configuration of Axios, how to change the global configuration, and how to differentiate instance configuration

Take a look back at the code above and the Axios base class

// lib/core/Axios.js
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}
Copy the code
// lib/axios.js
var defaults = require('./defaults');
// Create the default instance to be exported
var axios = createInstance(defaults);

// Factory for creating new instances
axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
Copy the code

Global configuration is a file that binds an instance to the axios object’s defaults property by creating a new instance. Subsequent instances created by axios.create are merged with axios.defaults and instantiated. Axios.defaluts becomes a global configuration, and changing this property affects the AXIos object and all instances derived from it, which has its own defaults object on it and only affects itself

Let’s take a look at the specific default configurations

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

function getDefaultAdapter() {
  var adapter;
  if (typeofXMLHttpRequest ! = ='undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  } else if (typeofprocess ! = ='undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  }
  return adapter;
}

var defaults = {
  adapter: getDefaultAdapter(),
  transformRequest: [...]. .transformResponse: [...]. .timeout: 0.xsrfCookieName: 'XSRF-TOKEN'.xsrfHeaderName: 'X-XSRF-TOKEN'.maxContentLength: -1.maxBodyLength: -1.validateStatus: function validateStatus(status) {
    return status >= 200 && status < 300; }}; defaults.headers = {common: {
    'Accept': 'application/json, text/plain, */*'}}; 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);
});
module.exports = defaults;
Copy the code

Some of the details are omitted here, and you can go to the source code for more details, but what you might think is that the original document is not the default configuration at all, it just lists all the available configurations and what values should be passed, and the actual default configuration is only the ones in the file above

  • adapter

    It matches the lib/adapters/xhr.js and lib/adapters/http.js files.

  • TransformRequest and transformResponse

    If you just want to add a new transform rule, I recommend adding a new transform rule based on the default:

    axios.defaults.transformRequest = [ ...axios.defaults.transformRequest, ...customerTransform ]

  • Timeout, xsrfCookieName, xsrfHeaderName, maxContentLength, maxBodyLength, validateStatus

    These configurations are easy to understand and won’t be described here

  • headers

    The default header has a general Accept header, and then distinguishes the request types. Only ‘POST ‘,’ PUT ‘, and ‘patch’ will have the default ‘content-Type ‘: Application /x-www-form-urlencoded’ application/json’

conclusion

In the second part, I will go into the core of the request request and the implementation of the interceptor. As a brick lifter, I have limited ability. If there are children who do not understand this part and feel that I did not describe clearly, you can discuss in the comments section