javascript请求-axios

axios是在实际项目中应用比较多的请求插件了,提供给咱们的前置请求配置、响应拦截配置,让咱们在实际应用中可以很好的利用;
node

为何要看axios源码呢?

不知道你们有没有这种时候,当身边的其余人都在看源码实现时候,本身却无从下手,看着超级多的逻辑,逐渐失去了兴趣,所以我发现我本身的看源码切入点,“好奇”,看着项目配置中的“响应拦截”、“请求拦截”等,由心而生想要去知道这样究竟是如何实现的呢? 带着这种“好奇”我打开了axios的源码。ios

在看源码以前,必要的内容是先过一遍axios的使用方法,这样带着问题,会记忆深入ajax

1.配置覆盖

axios的配置大概分为三种axios

1.1全局配置

即在插件内部的默认配置api

  • 其实更通俗而言就是默认的配置 默认配置均存在于default.js中,

image.png

图1 全局默认配置的位置

image.png

图2 全局默认配置

1.2实例配置

在进行项目搭建时候,一般使用的自定义的全局配置数组

        有的时候 在一个应用中须要实例化不一样的kxios对象,针对不一样的接口 有利于管理,每一个实例化对象度灰有本身的配置,能够经过全局配置进行初始化,或合并成一个新的配置项目,实例配置通常是咱们进行本身配置的。promise

const nAxios = axios.create({
  // baseURL: process.env.BASE_API,
  // timeout: 5000,
  withCredentials: true,
  headers: {'X-Requested-With': 'XMLHttpRequest'}
})
复制代码

1.3请求配置

在进行请求时候,对单个请求进行的配置浏览器

        同一个实例会有一些公用的配置项目,如baseUrl,可是不少时候,不一样的请求具体的配置是不同的,如url、method等,因此在请求的时候须要传入的配置与实例配置进行合并;         请求配置合并通常是这样子的?get请求的url ,后面的config都是咱们传入的请求配置,传入到项目中后,会merge 默认的配置,而后在进行请求;缓存

kxios.get('http://localhost:4000/',{
  baseURL:'http://localhost:4000/',
  methods:'get',
  headers:{
    'Content-Type':'333',
    'Type':333
  }
}).then((res)=>{
  console.log("请求后的then")
  console.log(res)
})
复制代码

1.4配置优先级

请求配置>实例配置> 全局配置markdown

2.建立请求过程

2.1 建立axios

axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
复制代码

2.2建立axios实例

image.png

图3 请求入口主要文件

该文件提供了axios的实例构造器,即工厂函数

function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);
  // instance 绑定axios的默认数据 instance 
  var instance = bind(Axios.prototype.request, context);
  // 复制axios的原型到实例中。此处主要是确保后续进行改变时候,不改变Axios的对象函数
  utils.extend(instance, Axios.prototype, context);
  //将实例绑定到Axios构造出得对象中
  utils.extend(instance, context);
  //instance 进行复制
  return instance;
}
复制代码
  1. 建立Axios的实例
  2. instance 绑定axios的默认数据instance
  3. axios的原型到实例中。此处主要是确保后续进行改变时候,不改变Axios的对象函数
  4. 将实例绑定到Axios构造出得对象中

之因此选择这样,建立实例,而后在复制原型方法,是为了扩展axios自己更多的调用功能,在Axios的函数中,具备复杂数据类型,保证每一个axios的实例在继承后都有独立的数据结构;

axios.get(url)
axios(url)
复制代码

在外界引用了axios的方法就能进行建立axios的实例,并获得初始的默认值 进行调用

var nAxios = axios.create({
  baseURL: process.env.BASE_API,
  timeout: 5000,
  withCredentials: true,
  headers: {'X-Requested-With': 'XMLHttpRequest'}
})
//调用函数
nAxios.get('http:locallhost:888')
复制代码

对于nAxios.get('http:locallhost:888')的执行的过程

  • axios中进行调用get方法
  • Axios.js中对Axios的原型都附加上了请求方法,请求调用的时候直接调用时候,先进行merge请求的配置,在进行调用config的方法

2.3 merge 配置

utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url
    }));
  };
});
复制代码

2.4 进行请求适配配置

请求时候进行请求的调度,即去适配请求

image.png

图4 dispatchRequest发起请求
  • dispatchRequest.js的功能分析

image.png

图5 dispatchRequest功能分析

2.5 发送请求的主要缘由

在浏览器环境中主要是经过ajax进行请求

image.png

图6 浏览器中xhr发送请求

在浏览器中进行发送请求主要仍是以来XMLHttpRequest

image.png

图7 xhr.js中主要功能

3.node可使用axios的缘由

咱们在实际的使用中,发现node环境和浏览器环境均可以使用axios,实际上是axios内部进行了适配;

  • 在default.js 中,就进行了适配的操做
//获取默认的请求适配器
function getDefaultAdapter() {
  var adapter;
  //若是当前存在XMLHttpRequest 这个对象,则进行XHr的额适配器
  if (typeof XMLHttpRequest !== 'undefined') {
    // 浏览器环境下使用XML这个适配器
    adapter = require('./adapters/xhr')
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // node 环境下使用http这个模块适配器
    adapter = require('./adapters/http');
  }
  return adapter;
}
复制代码

image.png

图8 axios适用于多个平台缘由

4.请求拦截器和响应拦截器原理

请求拦截器和响应拦截器帮助咱们在实际工做中去处理本身前置转换需求

4.1 拦截器做用

  • 当咱们发送数据的时候,可以拦截到发送内容并进行更改内容,适用于统一性的发送信息,好比token、自定义的请求头,经过统一封装的request函数为每一个请求添加统一的信息;
  • 但后期若是须要为某些 GET 请求设置缓存时间或者控制某些请求的调用频率的话,咱们就须要不断修改 request 函数来扩展对应的功能。此时,若是在考虑对响应进行统一处理的话,咱们的 request 函数将变得愈来愈庞大,也愈来愈难维护
  • 咱们能够按照功能把发送 HTTP 请求拆解成不一样类型的子任务,好比有用于处理请求配置对象的子任务,用于发送 HTTP 请求的子任务和用于处理响应对象的子任务。当咱们按照指定的顺序来执行这些子任务时,就能够完成一次完整的 HTTP 请求。

4.2 拦截器构造函数

/axios/lib/core/InterceptorManager.js

在该文件中定义了拦截器的构造函数InterceptorManager,用于响应拦截和请求拦截的使用

function InterceptorManager() {
  this.handlers = [];
}
//增长一个响应
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};
//移除某一个
InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};  

复制代码

4.3 建立请求拦截和响应拦截

应用在Axios的构造函数中

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(), //请求拦截器
    response: new InterceptorManager() // 响应拦截器
  };
}
复制代码

4.4 拦截器执行过程

请求拦截器和响应拦截器主要实现原理为Promise的链式执行方法;

// 拦截器中间件
  //dispatchRequest为默认的主要请求内容 dispatchRequest返回一个promise的请求对象
  var chain = [dispatchRequest, undefined];
  //让config配置promise中,用来层级传递配置
  var promise = Promise.resolve(config); 
  //请求拦截器
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    //将请求拦截器插入到 chain数组的前面,插入的原理实际上是 自定义配置时候越在后配置的先执行
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  // 响应拦截器 将响应拦截器push 到chain 的后面 这样会后执行
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  //chain的结构 =[请求拦截器1,请求拦截器2,主要请求,响应拦截器1,响应拦截器2]
  //执行chain中的方法
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }
复制代码

这样就造成了一个拦截器的Promise队列

image.png

图9 拦截器队列结构
  • 执行该队列结束请求

5.请求过程当中的数据转换transformData

  • 请求操做数据会在请求拦截器以前进行执行,而响应拦截器则会在响应拦截器以前进行调用,
  • 执行在promise的then中 dispatchRequest.js

5.1 transformRequest请求操做数据

image.png

图10 请求操做数据

5.2 响应操做数据transformResponse

image.png

图11 响应操做数据

转换函数

module.exports = function transformData(data, headers, fns) {
  /*eslint no-param-reassign:0*/
  utils.forEach(fns, function transform(fn) {
    data = fn(data, headers);
  });
//返回转换后的数据
  return data;
};
复制代码

6. 一个axios的流程

建立实例的时候+调用接口数据的时候

image.png

图12 一个axios的流程

7.取消请求

取消请求的逻辑均在axios/lib/cancel中,取消请求在配置中主要使用CancelToken进行控制,在请求中,也存在专门为CancelToken设置的方法, 在xhr.js中

if (config.cancelToken) {
      // Handle cancellation 
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
          return;
        }
        //中止当前的请求
        request.abort();
        //而且进行reject
        reject(cancel);
        // Clean up request
        request = null;
      });
    }
复制代码

cancelToken的方法 中主要的一个做用在于执行阻止请求,并进行取消函数,进行下一个then的调用

8.总结

看完axios ,本身收获仍是蛮多的,更多的是流程设计

  • 如何暴露更灵活的api供使用
  • 巧妙利用Promise的then原理增长请求和拦截控制

9.参考文档

axios的使用

相关文章
相关标签/搜索