Hello everyone, today I am Zhuge Xiaoyu. For front-end development children’s shoes. In both Vue and React development, everyone should use the axios library recommended by the official. (what? You said Anglur didn’t recommend it! This classmate, please go out, with the door thank you!

First of all, we know axios is a great request library (available in browsers and Node.js) because before it we used Ajax four steps, using jQ ajax. Since JQ itself is a library, Ajax is also a function that it encapsulates and contains in its library, which is not pure at all. Ajax said, “A great man can’t stay under a heavy roof!” I want independent mountain!! ** Then Axios came along and a pure request-only library appeared.

Axios is a PROMISe-based HTTP library, documented at github.com/axios/axios. Then after understanding the origin of the emergence of the big brother, we will formally complete the matter, because the later to read the source code, so I believe that you should be able to accept this move, because after knowing Axios ox B, you can hit the door to take hold of the dead. Steady as you go, Ollie!

The following 6 points are only for those who have not carefully scanned the AXIos configuration. If you are particularly familiar with axios, please bypass the source code and read it directly.

Axios understands and uses

1. Request configuration

{
    // Request the server URL
    url: '/user'.// method Specifies the method used to create the request
    method: 'get'
    
    // baseURL will automatically precede the URL unless the URL is an absolute one
    baseURL: 'https://some-domain.com/api/'
    
    // 'transformRequest' allows you to modify request data before sending it to the server
    // Can only be used with PUT, POST, and PATH request methods
    // The function in the following array must return a string, either an ArrayBuffer, or a Stream
    transformRequest: [function(data, headers) {
        // Perform any conversion on data
        return data;
    }]
    
    // 'transformResponse' allows modification of response data before passing it to then/catch
    transformResponse: [function(data) {
        // Perform any conversion on data
        return data
    }]
    
    // 'headers' is the custom request header to be sent
    headers: {	'X-Requested-With': 'XMLHttpRequest' }
    
    // 'params' is the URL parameter to be sent with the request
    // Must be a plain object or URLSearchParams object
    params: {
        ID: 12345
    }
    
    // 'paramsSerializer' is a function that serializes 'params'
    paramsSerializer: function(params) {
        return Qs.stringify(params, {arrayFormat: 'brackets'})}// 'data' is the data sent as the body of the request
    // Only the request methods PUT POST PATHCH
    // When 'transformRequest' is not set, one of the following types must be required:
   	// - string,plain object, ArrayBuffer,ArrayBufferView,URLSearchParams
    // - Browser specific: FormData, File, Blob
    // - Node exclusive: Stream
    data: {
        firstName: 'Fred'
    }
    
    // Specifies the number of milliseconds for the request to timeout (0 indicates no timeout)
    // Request timeout, request will be interrupted
    timeout: 1000
    
    // 'withCreadentials' indicates whether credentials are needed to span a request
    withCreadentials: true.// default -> false
    
    // Adapter allows you to customize processing requests to make testing easier
    adapter: function(config) {
    	/ * * /
    }    
    
    // 'auth' indicates that HTTP basic authentication should be used and credentials provided
    // This sets an 'Authorization' header that overrides any existing custom 'Authorization' set using 'hedaers'
    auth: {
        username: 'janedoe'.password: 's00pers3cret'
    }
    
    // 'responseType' indicates the data type of the server response, which can be 'arrayBuffer ', 'blob', 'document', or 'json'.
    responseType: 'json'.// 'xsrfCookieName' is the name of the cookie worth the XSRF token
    xsrfCookieName: 'XSRF-TOKEN'
    
    // 'onUploadProgress' allows processing of progress events for uploads
    onUploadProgress: function(progressEvent) {
        // Do whatever you want with the native progress event
    }
    
    // 'onDownloadProgress' allows progress events to be handled for downloads
    onDownloadProgress: function(progressEvent) {
        // Handling of native progress events
    }
    
    // 'maxContentLength' defines the maximum size allowed for response content
    maxContentLength: 2000
    
    // 'validateStatus' defines resolve or reject promis for a given HTTP response status code
    validateStatus: function(status) {
        return status >=200 && status <300
    }
    
    // 'maxRedirects' defines the maximum number of redirects followed in Node.js
    // If set to 0, no redirection will be followed
    maxRedirects: 5.// default
    
    // 'proxy' defines the host name and port of the proxy server
    // 'auth' indicates that HTTP basic authentication should be used to connect to the proxy and provide credentials
    // This will set a 'proxy-authorization' header to override any customizations already set using 'header'
    proxy: {
        host: '127.0.01'.port: 9000.auth: {
            username: 'mikeymike'.password: 'rapunz3l'}}// 'cancelToken' specifies the cancel token used to cancel the request
    cancelToken: new CancelToken(function(cancel) {})}Copy the code

2. Response structure

The response to a request contains the following information

{
    // 'data' is the response provided by the server
    data: {}
    
    // 'status' is the HTTP status information from the server response
    status: 200.// 'statusText' is the HTTP status information from the server response
     statusText: 'OK'
    
    // 'headers' is the header of the server response
    headers: {}
    
    // 'config' is the configuration information provided for the request
    config: {}
    
    request: {}}Copy the code

3. Axios characteristics

1. Asynchronous Ajax request library based on Promise.

2. It can be used on both the browser and node.

3. Support request/response interceptors.

4. Support cancellation requests.

5. Request/response data conversion.

6. Send multiple requests in batches.

4.axios.create(config)

1. Create a new AXIOS based on the specified configuration, that is, each new AXIOS has its own configuration

2. The new Axios just doesn’t have a way to cancel requests or batch requests, and everything else is the same syntax

3. Why design this syntax?

(1) Requirements: The configuration required by some interfaces in the project is different from that required by other interfaces. What should I do?

(2) Solution: Create two new AXIos, each with unique configuration, to be applied to interface requests with different requirements

5. Axios processing chain process

// Add request interceptor (callback function) -> Add after execute first
axios.interceptors.request.use(
    config= > {
        return config
    },
    error= > {
        return Promise.reject(error)
    }
)

// Add a response interceptor
axios.interceptors.response.use(
    response= > {
        return response
    },
    error= > {
        return Promise.reject(error)
    }
)
Copy the code

6. Cancel the request

let cancel  // The function used to save the cancellation request
getProducts() {
    // Cancel pending requests before you are ready to send them
	if( typeof cancel === 'function' ) {
    	cancel('Cancel request')
	}
	axios({
    	url: 'http://localhost:8000/products1'.cancelToken: new axios.CancelToken((c) = > { // c is the function used to cancel the current request
    	// Save the cancel function, which may need to cancel the current request later
    	cancel = c;
  	})
	}).then(
    	response= > {
        	cancel = null
        	consoel.log('Request successful')},error= > {
        	if(axios.isCancel(error)) {
            	console.log('Error cancelling request')}else { // Request error
            	cancel = null
        		console.log(error.message)
        	}    
    	}
	)
}

cancelReq() {
    if(type cancel === 'function') {
        cancel('Force cancel request')}else {
        console.log('No cancelable request')}}/ / call
cancelReq()
Copy the code

If you have not read the above information, please read it carefully. If you have not read it, please recite it.

  • Unidentified lucky water buddy: Hold on, I have a question

  • Zhugexiaoyu: ha, you have a problem?

  • Zhugexiaoyu: what problem?

  • Unknown lucky water friend: see source code how to do……

Now it’s time for our official service. Play to play, make, don’t take the source code joke. In fact, the source code like Axios is relatively simple, I think for the source code or do a superficial taste of the line, you talk about the second and third layer in the interview, the interviewer may think you are in the fifth layer. It is not recommended to stop doing something unless it is of great benefit. At this time, you can see more excellent open source projects and make more progress.

Read the axios source code

1. File directory

Dist -> Package the generated file

Examples -> examples

Lib -> core file code

Adapters -> Request the relevant folder

Http.js -> node Server sends HTTP request

Xhr.js -> the module that actually makes the request

Cancel -> Cancel the folder associated with the request

Cancer.js -> defines the Cancel constructor

Canceltoken. js -> defines a constructor that performs canceltoken-related

Iscancel. js -> Determines whether an error is a cancel type error

Core -> something at the core

Axios.js -> Axios constructor

Dispathrequest.js -> Distribute the request

Interceptormanager.js -> interceptor related

Helpers -> tool module

Axios.js -> expose the axios function externally

Defaults.js -> things related to default configuration

Index.js -> Outer entry file

2. Relationship between Axios and Axios

Syntax: Axios is not an instance of axios.

② Functionally speaking: Axios is an instance of Axios. (1. It has its own attributes. 2. There are methods on his prototype chain)

Axios is the function returned by the axios. Prototype function bind()

(4) Axios as an object has all the methods on the Axios prototype object and all the properties on the AXIos object

Source axios.js file

/**
 * Create an instance of Axios
 *
 * @param {Object} defaultConfig The default config for the instance
 * @return {Axios} A new instance of Axios
 */
function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);
  / / equivalent Axios. Prototype. Request. Bind (context)
  // Bind returns a new function that calls the request function internally,
  // The axios function finally finds request
  // Context is an instance of Axios. Once you use this in the request function, it points to an instance of Axios
  var instance = bind(Axios.prototype.request, context); // axios

  // Copy the method from the Axios prototype object to instance: request()/get()/post()/put()/delete()
  utils.extend(instance, Axios.prototype, context);

  // Copy context to instance
  // Copy the properties from the Axios instance object to instance: the defaults and Interceptors properties
  utils.extend(instance, context);

  return instance;
}

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

Copy the code

3. The difference between Instance and Axios?

1. The same:

(1) is a function that can send any request: request(config)

Get ()/post()/put()/delete()

(3) Both have default configurations and interceptors attributes: defaults/ Interceptors

2. The different:

(1) Default matching values are likely to be different

(2) Instance does not have some methods added after axios: create()/CancelToken()/all()

Source axios.js file

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

// Expose Cancel & CancelToken
// The difference is that axios adds these operations, while axios.create does not
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');

// Expose all/spread
axios.all = function all(promises) {
  return Promise.all(promises);
};
Copy the code

4. The overall process that AXIos runs?

Main process:

request(config) == > dispatchRequest(config) ===> xhrAdapter(config)

Request (config) :

Chain the request interceptor/dispatchRequest()/response interceptor through the Promise chain, then the Promise

dispatchRequest(config) :

Convert the request data ===> Call xhrAdapter() to send the request === => Convert the response data after the request returns. Return to the promise

xhrAdapter(config):

Create an XHR object, set it according to config, send a specific request, receive the response data, and return a Promise.

Axiso flowchart:

This chart to take the most original process, Win10 own drawing software. Ha ha

request(config)

Source code axios.js/axios.js file

Promise uses its chain of uses to connect the request interceptor, the action that makes the request, the response interceptor, and the success or failure of our final request

/**
 * Create an instance of Axios
 *
 * @param {Object} defaultConfig The default config for the instance
 * @return {Axios} A new instance of Axios
 */
function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);
  // Axios.prototype.request.bind(context)
  var instance = bind(Axios.prototype.request, context); // axios

  // Copy axios.prototype to instance
  // Copy the method from the Axios prototype object to instance: request()/get()/post()/put()/delete()
  utils.extend(instance, Axios.prototype, context);

  // Copy context to instance
  // Copy the properties from the Axios instance object to instance: the defaults and Interceptors properties
  utils.extend(instance, context);

  return instance;
}

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

// Expose Axios class to allow class inheritance
axios.Axios = Axios;

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

Copy the code
/* Dispatch a request ** We use axios, which is the function returned by bind()@param {Object} config The config specific for this request (merged with this.defaults)
 */
Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {
    config = arguments[1) | | {}; config.url =arguments[0];
  } else {
    config = config || {};
  }

  config = mergeConfig(this.defaults, config);

  // Set config.method
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get'; } -- -- -- -- -- -// Promise uses its chain of uses to connect the request interceptor, the action that makes the request, the response interceptor, and the success or failure of our thickest request

  // Hook up interceptors middleware
  // Undefined: the chain is a reject callback, which ensures that the interceptor will be called back in pairs
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);

  // Find all request interceptor functions. The last request interceptor is stored at the front of the array
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  // The added response interceptor is stored after the array
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  // All request interceptors/request method/response interceptors are concatenated via promise's then()
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  // Return the promise that specifies our onResolved and onRejected
  return promise;
}; 

Copy the code

dispatchRequest(config)

Source dispatchRequest. Js/default. Js file

/**
 * 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
 */
module.exports = function dispatchRequest(config) {
  throwIfCancellationRequested(config);

  // Ensure headers exist
  config.headers = config.headers || {};

  // Perform the necessary processing transformations on the data in config
  // Set the content-type request header
  // Transform request data
  config.data = transformData(
    config.data,
    config.headers,
    // Convert the data format
    config.transformRequest // -- corresponds to the defalut file
  );

  // Flatten headers
  // Consolidate all headers in config
  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) {
      deleteconfig.headers[method]; });var adapter = config.adapter || defaults.adapter;

  return adapter(config).then(function onAdapterResolution(response) {
    throwIfCancellationRequested(config);

    // Transform response data
    // Parse the unparsed data in response
    // Json string parses into JS objects/arrays
    response.data = transformData(
      response.data,
      response.headers,
      // Convert the data format
      config.transformResponse // -- corresponds to the defalut file
    );

    return response;
  }, function onAdapterRejection(reason) {
    if(! isCancel(reason)) { throwIfCancellationRequested(config);// Transform response data
      if(reason && reason.response) { reason.response.data = transformData( reason.response.data, reason.response.headers, config.transformResponse ); }}return Promise.reject(reason);
  });
};
Copy the code
// Get the request adapter corresponding to the current environment
  adapter: getDefaultAdapter(),

  // Request converter
  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 data is an object, specify the request body parameter format as JSON and convert the parameter data object to JSON
    if (utils.isObject(data)) {
      setContentTypeIfUnset(headers, 'application/json; charset=utf-8');
      return JSON.stringify(data);
    }
    returndata; }].// Response data converter: parses string data
  transformResponse: [function transformResponse(data) {
    /*eslint no-param-reassign:0*/
    if (typeof data === 'string') {
      try {
        data = JSON.parse(data);
      } catch (e) { /* Ignore */}}returndata; }].Copy the code

xhrAdapter(config)

Source xhr.js file

// Create an XHR object
    var request = new XMLHttpRequest();

    // HTTP basic authentication
    if (config.auth) {
      var username = config.auth.username || ' ';
      var password = unescape(encodeURIComponent(config.auth.password)) || ' ';
      requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
    }

    var fullPath = buildFullPath(config.baseURL, config.url);

    // Initialize the request
    request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
	// buildURL handles the request URL in the help folder buildurl.js

    // Set the request timeout in MS
    // Specify the timeout period
    request.timeout = config.timeout;

    // Listen for ready state
    // bind a listener for request status changes
    request.onreadystatechange = function handleLoad() {
      if(! request || request.readyState ! = =4) {
        return;
      }

      // The request errored out and we didn't get a response, this will be
      // handled by onerror instead
      // With one exception: request that using file: protocol, most browsers
      // will return status as 0 even though it's a successful request
      if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') = = =0)) {
        return;
      }

      // Prepare the response
      // Prepare the response object
      var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
      varresponseData = ! config.responseType || config.responseType ==='text' ? request.responseText : request.response;
      var response = {
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config: config,
        request: request
      };

      // Determine the result state (success/failure) of the request promise based on the response status code
      settle(resolve, reject, response); // settle. Js file below

      // Clean up request
      request = null;
    };

    // Handle browser request cancellation (as opposed to a manual cancellation)
    // Bind request interrupt listener
    request.onabort = function handleAbort() {
      if(! request) {return;
      }

      reject(createError('Request aborted', config, 'ECONNABORTED', request));

      // Clean up request
      request = null;
    };

	// Not all browsers support upload events
    // Bind the monitor of upload progress
    if (typeof config.onUploadProgress === 'function' && request.upload) {
      request.upload.addEventListener('progress', config.onUploadProgress);
    }

    // If cancelToken is configured
    if (config.cancelToken) {
      // Handle cancellation
      // Specifies the callback function used to interrupt the request
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if(! request) {return;
        }

        // Interrupt the request
        request.abort();
        // Make the request promise fail
        reject(cancel);
        // Clean up request
        request = null;
      });
    }

    if(! requestData) { requestData =null;
    }

    // Send the request
    // Send a request, specifying the request body data, possibly null
    request.send(requestData);
Copy the code

Settle. Js file

/**
 * Resolve or reject a Promise based on response status.
 *
 * @param {Function} resolve A function that resolves the promise.
 * @param {Function} reject A function that rejects the promise.
 * @param {object} response The response.
 */
module.exports = function settle(resolve, reject, response) {
  var validateStatus = response.config.validateStatus;
  if(! response.status || ! validateStatus || validateStatus(response.status)) {// The request succeeded
    resolve(response);
  } else {
     // Request error
    reject(createError( Createerror.js creates the error object function
      'Request failed with status code ' + response.status,
      response.config,
      null, response.request, response )); }};default.js file// Read status code: [200,299]
  validateStatus: function validateStatus(status) {
    return status >= 200 && status < 300;
  }
Copy the code

5. What is axios request/response data converter?

function

1. Request converter: a function that performs specific processing on request header and request body data

// If data is an object, specify the request body parameter format as JSON and convert the parameter data object to JSON
    if (utils.isObject(data)) {
      setContentTypeIfUnset(headers, 'application/json; charset=utf-8');
      return JSON.stringify(data);
    }
Copy the code

2. Response converter: A function that parses the response body JSON string into A JS object or array

if (typeof data === 'string') {
      try {
        data = JSON.parse(data);
      } catch (e) { /* Ignore */}}Copy the code

6. Overall structure of Response

 {
        data,
        status,
        statusText,
        headers,
        config,
        request
};
// Corresponds to xhr.js in the adapters folder
Copy the code

7. Error overall structure

{
   config,
   request,
   response,
   ...
}
// enhanceerror.js corresponds to the source core folder
Copy the code

8. How do I cancel an outstanding request?

1. When the cancelToken object is configured, save the cancel function

(1) Create a cancelPromise for future interrupt requests

(2) And define a cancel function to cancel the request

(3) Pass the cancel function

2. Call Cancel () to cancel the request

(1) Execute cancel function to pass error message message

(2) Internally the cancelPromise will be made a success, and the success value is a Cancel object

(3) Interrupt the request in the successful callback of cancelPromise and let the promise fail. The reason for the failure is the Cancel object

Canceltoken.js/xhr.js

function CancelToken(executor) {
  if (typeofexecutor ! = ='function') {
    throw new TypeError('executor must be a function.');
  }

  // Prepare a Promise object for the cancellation request, and save the resolve function to the outside.
  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  // Save the current token object
  var token = this;
  // The executor function is externally defined and internally called
  // Execute the received executor function immediately, passing in the cancel function to cancel the request
  // The cancel function is internally defined, externally called - "combined with the above (Axios understands using point 6 together with Kangkang)
  executor(function cancel(message) {
    // If there is a reason in the token, the request has been cancelled. | see, after this if see the token below. The first reason
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }

    // Specify the token's reason as a Canel object
    token.reason = new Cancel(message);
    // Specify the promise of the cancellation request as success with a value of reason
    resolvePromise(token.reason);
  });
}
Copy the code
	// If cancelToken is configured
    if (config.cancelToken) {
      // Handle cancellation
      // Specifies the callback function used to interrupt the request
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if(! request) {// If the request is not finished, you can follow the interrupt request
          // If the request is complete, return directly, unable to interrupt the following sequence of operations
          return;
        }

        // Interrupt the request
        request.abort();
        // Make the request promise fail
        reject(cancel);
        // Clean up request
        request = null;
      });
    }
Copy the code

— — — — — — — — — — — — — — — — — — — — — — — –

conclusion

If you are interested in the above code, clone my code on gitHub and send kohane a chicken leg (like, comment, STRT) if you think it will help you

Github has more content than this article, so don’t miss it.