写在前面:

   距离2021年 还有两个月~11月份的开始,决定勤奋一波 

  axios 不用更多的介绍,vue官方的推荐是使用axios,vue-resource淡出框架的依赖,在我们进行项目开发和搭建的时候,axios是我们连接和后端接口的桥梁,当然选择axios,自然是其的一些特点,能够更好的满足我们日常的工作;

能学习到什么呢 

 通过本篇文章,你将大概了解到axios创建实例过程、配置合并流程以及原理,还有进行请求的流程;深入了解到axios的响应拦截器原理,支持多平台(浏览器和node)环境下使用原理、请求响应参数的设置原理、整个的执行过程~嘻嘻,有些地方大家简单大概了解,还是需要看下源码的执行原理滴;

进入axios 

  想要了解axios的原理,可以从其特点出发; 

  我和数据差个“axios" -- axios的原理解析

 

 

 

 

 

进入原理探究过程(搓手手)

我和数据差个“axios" -- axios的原理解析

首先自行下载axios的包,查看其目录结构

在这个里面有两个助手包,一个helpers、一个是utils,这两个包都是axios的工具包,helpers是服务于axios,utils更加广泛的工具,可以在其他的插件中进行引入使用,我们每个插件其实都会自定义自己的工具包~

我和数据差个“axios" -- axios的原理解析

  配置阶段

 axios的配置共分为3种,全局配置、实例配置、请求配置;这三个配置和我们进行的操作息息相关;

全局配置

全局配置,更加明确的说其实是axios里面给我们提供的一个默认的选项,当我们导入axios的包的时候,其实就已经进行了默认选项的配置;

默认配置项如下:

我和数据差个“axios" -- axios的原理解析 

 而这些全局配置选项也正是我们在进行实例化的时候,可以进行自定义配置的,主要的属性包括;

  • transformRequest :Array 允许在向服务器前发送信息的时候,统一处理请求信息默认配置选项功能: 
    • 序列化请求参数
    • 为不同类型的请求参数添加请求头
  • 我和数据差个“axios" -- axios的原理解析
  • transformResponse :Array 则是对响应数据的操作处理 

  • timeout 、headers等默认的配置
  • 默认配置如图所示
  • 我和数据差个“axios" -- axios的原理解析 

实例配置

  实例配置其实是我们针对自己项目进行的个性化的配置,在导入axios时候,就给我们提供了一个create的函数,而这个函数的作用其实是创建一个新的实例,并返回给我们;应用于整个项目配置

在一个项目中每个接口都有共同的配置,或者几个接口有共同的表现形式,每个实例化的axios都有自己的配置,通过全局配置进行初始化,或者合并成一个新的配置项目实例化配置一般是我们自己配置的,我们在项目中可能包含多个模块,项目的接口名字不一致,因此需要配置不同的axios的实例信息,这样我们配置moduleA和moduleB的实例配置,每次返回的新的实例不会互相影响;

 我和数据差个“axios" -- axios的原理解析

 create的方法

axios.create = function create(instanceConfig) {  
 // console.log("实例配置",mergeConfig(axios.defaults, instanceConfig))
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
//定义创建的axios
function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig); 
  // instance 绑定axios的默认数据 instance  返回wrap函数
  var instance = bind(Axios.prototype.request, context); 
  // console.log(instance.prototype)
  // 复制Axios的原型到扩展到实例中
  utils.extend(instance, Axios.prototype, context); 
  //将Axios的属性扩展到实例上
  utils.extend(instance, context); 
  //instance 进行复制
  return instance;
}

这块儿其实有点难以理解 ,就是我们创建实例时候,直接new Axios就可以了,为什么还需要进行包装呢??

先来看bind方法 ,bind的方法其实很好理解,就是将Axios原型方法上的request方法绑定在context的上下文中,返回一个wrap的方法

module.exports = 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);
  };
};
》》 utils.extend(instance, Axios.prototype, context); 这个的意思又是什么呢
function extend(a, b, thisArg) {
  // console.log("参数b",b)
  forEach(b, function assignValue(val, key) {
    if (thisArg && typeof val === 'function') {
      a[key] = bind(val, thisArg);
    } else {
      a[key] = val;
    }
  });
  return a;
}

迷糊人员?这里其实是将Axios上原型上的方法复制到我们的instance的实例上,这样的操作?保持迷惑?

最后一个的extend  utils.extend(instance, context);  是将刚刚创建的Axios的实例,复制到instance上,整个创建实例包含的步骤有:

 

  • 将b的属性内容复制给a
  • 此时将Axios原型上的方法复制到instance中
  • 最后把axios实例复制给instance 形成一个真正的instance

 

查阅资料,说这样的操作是为了更好的使用axios,如果我们只是返回一个new Axios的实例,那么我们在进行调用的方式是比较单一的,这样子配置了后;调用的方式就多了起来
axios({config}).then()
axios.get('url').then()

这种的实现方式,利用了拷贝继承,打印instance的构造函数;

我和数据差个“axios" -- axios的原理解析

 

 

 请求配置

同一个实例会有一些公用的配置项目,如baseUrl,但是很多时候,不同的请求具体的配置是不一样的,如url、method等,所以在请求的时候需要传入的配置与实例配置进行合并;

我和数据差个“axios" -- axios的原理解析

 

 请求的时候,也进行了相关的配置项目,这样形成了三个配置项,主要采用后配置后优先的原则,优先级顺序:请求配置>实例配置>全局配置;

这个时候便涉及到了合并的问题;主要涉及的配置合并

我和数据差个“axios" -- axios的原理解析

  

配置合并主要在/lib/core/mergeConfig.js中进行

module.exports = function mergeConfig(config1, config2) {
  // eslint-disable-next-line no-param-reassign
  config2 = config2 || {};
  var config = {};
 

  var valueFromConfig2Keys = ['url', 'method', 'params', 'data'];
  //需要进行深拷贝的属性
  var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy'];
  //默认的配置项目key
  var defaultToConfig2Keys = [
    'baseURL', 'url', 'transformRequest', 'transformResponse', 'paramsSerializer',
    'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
    'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress',
    'maxContentLength', 'validateStatus', 'maxRedirects', 'httpAgent',
    'httpsAgent', 'cancelToken', 'socketPath'
  ];
  
  //将传入的配置项目内容先赋值到config中
  utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {
    if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    }
  });
  // 遍历需要进行深拷贝的属性
  /**
   *  config2中的如果是对象 则进行深拷贝 
   *  如果不是则直接进行赋值,如果该属性对应的值,
   *  则直接进行拷贝config1的内容
   */
  utils.forEach(mergeDeepPropertiesKeys, function mergeDeepProperties(prop) {
   if (utils.isObject(config2[prop])) {
      config[prop] = utils.deepMerge(config1[prop], config2[prop]);
    } else if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    } else if (utils.isObject(config1[prop])) {
      config[prop] = utils.deepMerge(config1[prop]);
    } else if (typeof config1[prop] !== 'undefined') {
      config[prop] = config1[prop];
    }
  });
   //defaultToConfig2Keys 一些配置 config2的内容存在则使用config2的,不存在使用config1的内容
  utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {
    if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    } else if (typeof config1[prop] !== 'undefined') {
      config[prop] = config1[prop];
    }
  });
  //请求的一些key
  var axiosKeys = valueFromConfig2Keys
    .concat(mergeDeepPropertiesKeys)
    .concat(defaultToConfig2Keys);

  //其他的配置key 传入的key的值
  var otherKeys = Object
    .keys(config2)
    .filter(function filterAxiosKeys(key) {
      return axiosKeys.indexOf(key) === -1;
    });
  // 将config2中的自定义key的值 存入config中
  utils.forEach(otherKeys, function otherKeysDefaultToConfig2(prop) {
    if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    } else if (typeof config1[prop] !== 'undefined') {
      config[prop] = config1[prop];
    }
  });
  //返回config
  return config;
};
View Code

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-01-08
  • 2021-07-02
  • 2022-12-23
  • 2022-01-08
  • 2022-01-08
  • 2021-06-22
相关资源
相似解决方案