preface

In daily development, when we use AXIos to pass parameters, we often get confused about where the parameters are passed because AXIos does merge compatibility with the parameters we pass. Now let’s look at the use of axios parameters from the source level.

Three ways to use Axios

  • axios.request(config)
  • axios.get(url,config)/axios.post(url,data,config)
  • axios(config)

Axios returns a method, iterates through all methods on axios. Prototype, and assigns values to axios, so axios can support multiple calls.

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;
}
Copy the code

Parameter merge for Axios

Axios receives only a config object that contains the url, method, params, body, headers and other parameters that are commonly used. It can be passed separately because the parameters are merged internally.

utils.forEach(['delete'.'get'.'head'.'options'].function forEachMethodNoData(method) {
  Axios.prototype[method] = function(url, config) {
    // merge into a config object
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: (config || {}).data
    }));
  };
});

utils.forEach(['post'.'put'.'patch'].function forEachMethodWithData(method) {
  Axios.prototype[method] = function(url, data, config) {
  // merge into a config object
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});
Copy the code

Params and data arguments to Axios

The params parameter of AXIos is simply processed, concatenated to the URL, and then requested the interface. The data parameter is directly used as the body, and passed to the background interface through xhr.send(data). The source code is as follows:

  • Params parameter processing
var request = new XMLHttpRequest();
var fullPath = buildFullPath(config.baseURL, config.url);
request.open(config.method.toUpperCase(), buildURL(fullPath, config.params), true);
// ... 
function buildURL(url, params) {
  var serializedParams;
  var parts = [];
  utils.forEach(params, function serialize(val, key) {
    if (val === null || typeof val === 'undefined') return;
    // Handle the array parameters
    if (utils.isArray(val)) { 
      key = key + '[]';
    } else {
      val = [val];
    }
    // Handle date arguments and ordinary objects
    utils.forEach(val, function parseValue(v) {
      if (utils.isDate(v)) {
        v = v.toISOString();
      } else if (utils.isObject(v)) {
        v = JSON.stringify(v);
      }
      parts.push(encode(key) + '=' + encode(v));
    });
  });
  // Concatenate parameters to the URL
  serializedParams = parts.join('&');
  if (serializedParams) {
    var hashmarkIndex = url.indexOf(The '#');
    if(hashmarkIndex ! = = -1) {
      url = url.slice(0, hashmarkIndex);
    }
    url += (url.indexOf('? ') = = = -1 ? '? ' : '&') + serializedParams;
  }
  return url;
};
Copy the code
  • The data parameter
module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    var requestData = config.data;
    var request = new XMLHttpRequest();
    // ...request.send(requestData); })}Copy the code

Headers default argument for Axios

Headers of the parameters of axios axios support according to your different request method, set up different default parameters, via axios.defaults.headers.com mon = {XXX: “XXX “} Settings,axios arguments support axios.defaults Settings, but have lower precedence than the passed config arguments

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({
    'Content-Type': 'application/x-www-form-urlencoded'
  });
});
Copy the code

Axios interceptor and adapter

The basic use

/ / the interceptor
axios.interceptors.request.use(config= > { 
  config.headers = { 
    'Content-Type': 'application/x-www-form-urlencoded' // Configure the request header
  } 
  return config; 
}, error= >{});/ / adapter
axios.interceptors.response.use(response= > {
  response.data = "XXX"
  return response
}, error= >{})Copy the code

Implementation idea:

  • The use method pushes the incoming callback into the interceptor and adapter arrays, respectively
  • Create an array of ajax methods preceded by the unshift interceptor callback and followed by the push adapter callback
  • The order of calls is guaranteed through the chain calls of promise.then
  • Thus, the corresponding parameters are invoked before each request is sent (interceptor) and after each request is successful (adapter).

The core source code is as follows:

// The adapter and interceptor use the same constructor to implement a use method
function InterceptorManager() {
  this.handlers = [];
}
InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};

// The instance is mounted on axios.interceptors
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(), / / the interceptor
    response: new InterceptorManager() / / adapter
  };
}


Axios.prototype.request = function request(config) {
  var requestInterceptorChain = [];
  // The interceptor's function collects the array
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
    
  // The adapter's function collects the array
  var responseInterceptorChain = [];
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
  });
    
  var promise;
  // Send ajax requests in the middle
  var chain = [dispatchRequest, undefined];
  // Interceptor functions come before Ajax
  Array.prototype.unshift.apply(chain, requestInterceptorChain);
  // The adapter functions come after Ajax
  chain.concat(responseInterceptorChain);
  // We pass config as the function argument, and response as the function argument after dispatchRequest
  promise = Promise.resolve(config);
  // concatenate all functions with promise.then
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }
  return promise;
};
Copy the code

Axios cancel request

Two ways of use

  • Pass in a cancelToken argument whose value is an instance of the cancelToken function
  • Save the resolve function and execute it if necessary
/ / way
const source = axios.CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
});
// Cancel the request
source.cancel();

2 / / way
let cancel;
axios.get('/user/12345', {
  cancelToken: new axios.CancelToken(function executor(c) { cancel = c; })});// Cancel the request
cancel();
Copy the code

The source code to achieve

The core idea is that promise+xhr.abort is implemented

  • Create an instance of CancelToken, passing in a callback function
  • A Promise is mounted on the instance, and the promise’s resolve method is passed as an argument
  • When sending an XHR request, register promise.then to interrupt the request
  • Execute promise’s resolve method outside, breaking the request
function CancelToken(executor) {
  var resolvePromise;
  // Create a Promise object
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });
  
  // Pass promise's resolve method when the constructor executes
  executor(function cancel() {
    resolvePromise();
  });
}
CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    var request = new XMLHttpRequest();
    request.open(config.method.toUpperCase(), true);
    // Native JS onabort method
    request.onabort = function handleAbort() {
      reject();
    };
    // ...
    if (config.cancelToken) {
    // The promise created here is waiting to be executed
      config.cancelToken.promise.then(function onCanceled(cancel) {
        request.abort();
        reject(cancel);
      });
    }

    request.send(requestData);
  });
};
Copy the code

Upload and download progress monitoring method

The basic use

  • Use the same method as passing headers or other parameters to config.
axios({
  onUploadProgress: function (progressEvent) { 
    // Do whatever you want with the native progress event 
  }, 
  // 'onDownloadProgress' allows progress events to be processed for downloads
  onDownloadProgress: function (progressEvent) { 
  // Handling of native progress events}}})Copy the code

The source code to achieve

Here the source code implementation is very simple, directly call XHR two native corresponding methods

function xhrAdapter(config) {
      return new Promise(function dispatchXhrRequest(resolve, reject) {
        var request = new XMLHttpRequest();
        request.open(config.method.toUpperCase(), url, true);

        // Handle progress if needed download progress
        if (typeof config.onDownloadProgress === 'function') {
          request.addEventListener('progress', config.onDownloadProgress);
        }

        // Not all Browsers support Upload events Upload progress
        if (typeof config.onUploadProgress === 'function' && request.upload) {
          request.upload.addEventListener('progress', config.onUploadProgress);
        }

        // Send the request
        request.send(requestData);
      });
    };
Copy the code

The last

The implementation of AXIos, from the source point of view, is not very difficult, the source code posted in the article is some deleted, interested friends, you can refer to the source code to see the specific implementation, using Axios in work more smooth, finally, if the article is helpful to you, don’t forget to like oh ~