Axios运行过程的一个解析

540 阅读2分钟

前言

之前在写小程序的时候就觉得没有拦截器很不方便,于是就看了看源码,这里只是针对性的去看了解axios的执行过程,拦截器怎么写的,这篇文章的分析只是说axios的一些运行过程,如果想了解全面点,可以看这里,我觉得写得还可以

axios的使用

// 这里引入axios的时候就已经创建了axiso的实例了,并且返回一个函数
import axios from "axios";


// 创建一个axios实例
// 使用create会把参数进行合并
const service = axios.create({
  timeout: 6000,
})

// 设置请求拦截
service.interceptors.request.use(config => {
  return config
}, error => {
  return Promise.reject(error)
})

// 设置响应拦截

service.interceptors.response.use(res => {
  return res
}, err => {
  return Promise.reject(err)
})


service({
    url: 'xxx/xxx',
    method: 'post',
    data
})

我们从这种axios.create的创建方式来分析执行过程

从入口出发

node__module里面找到axios/lib/axios这里是创建的入口,当我们想要了解axios一般都会打印一下里面是什么的吧

warp.png

结果返回的是个函数,这时你会想怎么不是应该像vue,jQuery那样返回一个对象嘛,这不对呀, 那他的create从哪里来,不急我们像用console.dir打印一下这个方法里面的属性

dir.png

这里确实绑定了很多属性,但好像还是找不到create,没关系,我们来看看源码

// lib/axios.js
'use strict';

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

/**
 * Create an instance of Axios
 * 第二步:创建一个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);
  // 第三步: 外面serve()执行的就是这个request
  var instance = bind(Axios.prototype.request, context);
  
  --------- 代码跳转
  // lib/helpers/bind.js
  // 调用createInstance(defaults)时候就返回wrap方法 所以在打印的时候看到的就是这个
  function bind(fn, thisArg) {
      return function wrap() {
        var args = new Array(arguments.length);
        for (var i = 0; i < args.length; i++) {
          args[i] = arguments[i];
        }
        return fn.apply(thisArg, args);
      };
};
 ---------
 
    
  // Copy axios.prototype to instance 继承context
  utils.extend(instance, Axios.prototype, context);

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

  // 使用create递归createInstance方法
  // 合并参数 重新创建实例
  instance.create = function create(instanceConfig) {
    return createInstance(mergeConfig(defaultConfig, instanceConfig));
  };

  return instance; // 返回一个函数
}

// Create the default instance to be exported
// 执行 request
// 第一步:一开始就创建实例暴露出去了 所以打印的时候就看到了axios是个函数
var axios = createInstance(defaults);

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

// Expose Cancel & CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
axios.VERSION = require('./env/data').version;

// Expose all/spread
axios.all = function all(promises) {
  return Promise.all(promises);
};
axios.spread = require('./helpers/spread');

// Expose isAxiosError
axios.isAxiosError = require('./helpers/isAxiosError');

module.exports = axios;

// Allow use of default import syntax in TypeScript
module.exports.default = axios;

request.js

然后进入lib/core/Axios核心库,上面已经new Axiosdefaults就是他的配置,也定义了拦截器,最重要的还是request方法

// lib/core/Axios.js
'use strict';

var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
var InterceptorManager = require('./InterceptorManager');
var dispatchRequest = require('./dispatchRequest');
var mergeConfig = require('./mergeConfig');
var validator = require('../helpers/validator');

var validators = validator.validators;
/**
 * Create a new instance of Axios
 *
 * @param {Object} instanceConfig The default config for the instance
 */
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  // 拦截器
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

/**
 * Dispatch a request
 * 
 * @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';
  }

  var transitional = config.transitional;

  if (transitional !== undefined) {
    validator.assertOptions(transitional, {
      silentJSONParsing: validators.transitional(validators.boolean),
      forcedJSONParsing: validators.transitional(validators.boolean),
      clarifyTimeoutError: validators.transitional(validators.boolean)
    }, false);
  }
----------------上面可以不用管 都是些边界 合并参数问题
  // 这里存放的是请求拦截的处理函数
  var requestInterceptorChain =  [];
  
  var synchronousRequestInterceptors = true;
   
  // 这个forEach执行的是new InterceptorManager()里面的方法
  // 作用是 把unshiftRequestInterceptors这个方法作为回调参数 获取interceptor
  // interceptor就是拦截器的对象
  this.interceptors.request.forEach(
    function unshiftRequestInterceptors(interceptor) {
     // 边界问题
      if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
      return;
    }

    // synchronousRequestInterceptors=false
    synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;

    // 每个use放到一个requestInterceptorChain数组里
    requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
    
   // 响应拦截
  var responseInterceptorChain = [];
  this.interceptors.response.forEach(
    function pushResponseInterceptors(interceptor) {
    responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
  }
  );

  var promise;

  if (!synchronousRequestInterceptors) {
      // dispatchRequest是发送请求的方法
    var chain = [dispatchRequest, undefined];
    
    
    // 向chain开头添加resolve reject回调 
    Array.prototype.unshift.apply(chain, requestInterceptorChain);

    // 拼接响应resolve reject回调 
    //  [requestInterceptorChain,dispatchRequest, undefined,requestInterceptorChain]
    chain = chain.concat(responseInterceptorChain);

    
    promise = Promise.resolve(config);

    while (chain.length) {
      // 执行then 获取config 传给 resolve reject
      promise = promise.then(chain.shift(), chain.shift());
    }
    
    return promise;
  }


  var newConfig = config;
  while (requestInterceptorChain.length) {
    var onFulfilled = requestInterceptorChain.shift();
    var onRejected = requestInterceptorChain.shift();
    try {
      newConfig = onFulfilled(newConfig);
    } catch (error) {
      onRejected(error);
      break;
    }
  }

  try {
    promise = dispatchRequest(newConfig);
  } catch (error) {
    return Promise.reject(error);
  }

  while (responseInterceptorChain.length) {
    promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
  }

  return promise;
};

//...省略

module.exports = Axios;

InterceptorManager拦截器

'use strict';

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

function InterceptorManager() {
  this.handlers = [];
}

/**
 * Add a new interceptor to the stack
 * 第一步:使用use的时候会把fulfilled,rejected的处理函数push到一个数组里面
 * @param {Function} fulfilled The function to handle `then` for a `Promise`
 * @param {Function} rejected The function to handle `reject` for a `Promise`
 *
 * @return {Number} An ID used to remove interceptor later
 */
InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected,
    synchronous: options ? options.synchronous : false,
    runWhen: options ? options.runWhen : null
  });
  return this.handlers.length - 1;
};

/**
 * Remove an interceptor from the stack
 *
 * @param {Number} id The ID that was returned by `use`
 */
InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};

/**
 * Iterate over all the registered interceptors
 * 第二步:循环this.handlers获取里面的对象
 * This method is particularly useful for skipping over any
 * interceptors that may have become `null` calling `eject`.
 *
 * @param {Function} fn The function to call for each interceptor
 */
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    // h -> this.handlers里面的 {fulfilled,rejected}
    if (h !== null) {
      fn(h);
    }
  });
};

module.exports = InterceptorManager;

一个基本的流程图就是这样

流程图

写着最后

语言组织可能不太好,但是这也算是我个人的一个理解,路还很长,一个小目标一步一步来实现就好了