Fancy Front End Fancy Front End
  • 开始上手
  • 基础
  • 调度器(Scheduler)
  • 更新器(Updater)
  • 渲染器(Render)
  • 更新周期
  • hooks 原理
  • 总结
  • 📙 React源码漂流记
  • 开始上手
  • 基础
  • reactivity
  • runtime-core
  • runtime-dom
  • Awesome Web
  • Awesome NodeJS
话题
  • 导航
  • Q&A
  • 幻灯片
  • 关于
  • 分类
  • 标签
  • 归档
博客 (opens new window)
GitHub (opens new window)

Jonsam NG

让有意义的事变得有意思,让有意思的事变得有意义
  • 开始上手
  • 基础
  • 调度器(Scheduler)
  • 更新器(Updater)
  • 渲染器(Render)
  • 更新周期
  • hooks 原理
  • 总结
  • 📙 React源码漂流记
  • 开始上手
  • 基础
  • reactivity
  • runtime-core
  • runtime-dom
  • Awesome Web
  • Awesome NodeJS
话题
  • 导航
  • Q&A
  • 幻灯片
  • 关于
  • 分类
  • 标签
  • 归档
博客 (opens new window)
GitHub (opens new window)
  • 开始上手
  • Plan 计划
  • typescript-utility

  • single-spa源码

  • qiankun源码

  • webpack

  • axios

    • 开始阅读
    • core

      • 本章概要
      • axios 对象和方法
        • Axios 对象
        • 任务链
        • dispatchRequest
      • adapter 适配器
      • interceptor 拦截器
      • cancel 取消请求
  • solid

  • vite源码

  • jquery源码

  • snabbdom

  • am-editor

  • html2canvas

  • express

  • acorn源码

  • immutable.js源码

  • web
  • axios
  • core
jonsam
2022-06-23
目录

axios 对象和方法

# Axios 对象

Axios 对象通过 ES5 语法构建。

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}
1
2
3
4
5
6
7

从 OOP 的角度看,和 ES6 语法相比,类的构造器、实例方法、静态方法写法不同,可以作如下对比:

ES6 ES5
构造器 constructor 函数 函数体
调用福构造器 super() SuperClass.call(this)
静态方法、静态属性 static property Class.property
实例方法、实例属性 this.property Class.prototype.property
实例化 new Class() new Class()
this 指向实例 指向实例
继承 extends 关键字 原型继承

ES5 中类的继承:

/** Inherit the prototype methods from one constructor into another */
function inherits(constructor, superConstructor, props, descriptors) {
  constructor.prototype = Object.create(superConstructor.prototype, descriptors);
  constructor.prototype.constructor = constructor;
  props && Object.assign(constructor.prototype, props);
}
1
2
3
4
5
6

如何使用 ES5 语法继承父类?

  • 根据父类的原型创建一个原型,并作为子类的原型。
  • 将子类的构造器添加到子类的原型上。
  • 在子类的原型上添加子类额外的实例方法和实例属性。

# 任务链

在 axios 中,将 request 拦截器、dispatchRequest (发送请求)、response 拦截器按照 FIFO(先进先出,队列顺序)构成任务链,并且根据任务的同步或者异步,区分为同步任务链和异步任务链来进行处理。这种处理方法,对于程序的可扩展性、灵活性有很大的益处,可谓是眼睛一亮。

事实上,在很多其他类型需要对每个行为进行横向扩展,如拦截、代理、任务链、过滤的情境中,都可以参考这种思想。同时这种链式处理思想在责任链设计模式、代理模式中都或多或少的有所体现,在 single-spa 等源码中也能看到类似的使用方法。另外在 transformData 中也体现了这种思想:

/**
 * Transform the data for a request or a response
 *
 * @param {Object|String} data The data to be transformed
 * @param {Array} headers The headers for the request or response
 * @param {Number} status HTTP status code
 * @param {Array|Function} fns A single function or Array of functions
 *
 * @returns {*} The resulting transformed data
 */
module.exports = function transformData(data, headers, status, fns) {
  var context = this || defaults;
  /*eslint no-param-reassign:0*/
  utils.forEach(fns, function transform(fn) {
    data = fn.call(context, data, headers, status);
  });

  return data;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

下面我们来着重看下 Axios.prototype.request 方法的代码:

/**
 * Dispatch a request
 *
 * @param {String|Object} configOrUrl The config specific for this request (merged with this.defaults)
 * @param {?Object} config
 *
 * @returns {Promise} The Promise to be fulfilled
 */
Axios.prototype.request = function request(configOrUrl, config) {
  // ......
  config = mergeConfig(this.defaults, config);
  // ......

  // filter out skipped interceptors
  var requestInterceptorChain = [];
  var synchronousRequestInterceptors = true;
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
      return;
    }

    synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;

    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) {
    var chain = [dispatchRequest, undefined];

    Array.prototype.unshift.apply(chain, requestInterceptorChain);
    chain = chain.concat(responseInterceptorChain);

    promise = Promise.resolve(config);
    while (chain.length) {
      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;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

这里有一下几点技巧值得我们注意:

  1. 任务 request 都需要经过 interceptors.request 、 dispatchRequest 和 interceptors.response 的链式处理,根据 interceptors.request 是否有异步的拦截器,可以将其同步执行或者异步执行。
  2. 以异步执行为例,整个 chain 中的结构为 requestInterceptorChain + [dispatchRequest, undefined] + responseInterceptorChain ,每个 interceptorChain 中包含 onFulfilled 和 onRejected ,分别用于当前拦截器处理成功的回调和失败的回调。那个为什么 dispatchRequest 后面要加一个 undefined 呢?这是因为 chain 中每次出队列都是两个,因此整个队列在 promise 的驱动下才得以运行。
  3. Array 的 push、concat、splice、unshift 方法支持传入多个 item 的用法。可以看下如下的类型:
interface Array<T> {
  push(...items: T[]): number;
  concat(...items: ConcatArray<T>[]): T[];
  concat(...items: (T | ConcatArray<T>)[]): T[];
  splice(start: number, deleteCount: number, ...items: T[]): T[];
  unshift(...items: T[]): number;
  // ......
1
2
3
4
5
6
7

# dispatchRequest

/**
 * 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) {
 //  ......
 // Transform request data
 // 利用 transformRequest 对 data 经过处理
 config.data = transformData.call(
  config,
  config.data,
  config.headers,
  null,
  config.transformRequest
 );
 // ......

 utils.forEach(
  ["delete", "get", "head", "post", "put", "patch", "common"],
  function cleanHeaderConfig(method) {
   delete config.headers[method];
  }
 );

 var adapter = config.adapter || defaults.adapter;

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

   // Transform response data
   response.data = transformData.call(
    config,
    response.data,
    response.headers,
    response.status,
    config.transformResponse
   );

   return response;
  },
  function onAdapterRejection(reason) {
   if (!isCancel(reason)) {
    throwIfCancellationRequested(config);

    // Transform response data
    if (reason && reason.response) {
     reason.response.data = transformData.call(
      config,
      reason.response.data,
      reason.response.headers,
      reason.response.status,
      config.transformResponse
     );
    }
   }

   return Promise.reject(reason);
  }
 );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

这里有几点需要注意:

  • 2 个 transformer: transformRequest 和 transformResponse 的 transformer 管道。
  • adapter:发送请求的适配器。因为 axios 要适配各种 runtime,如 web 环境和 Node 环境,并且掩盖各种浏览器的关于发送请求得差异,因此采用适配器来处理请求发送。除此之外,这么做还有另一个好处,那就是提供用于自定义 adapter 的能力,帮助开发者是够适配各种非内置的运行环境中的请求发送的需求。
编辑 (opens new window)
上次更新: 2022/09/06, 14:25:16
本章概要
adapter 适配器

← 本章概要 adapter 适配器→

最近更新
01
渲染原理之组件结构与 JSX 编译
09-07
02
计划跟踪
09-06
03
开始上手
09-06
更多文章>
Theme by Vdoing | Copyright © 2022-2022 Fancy Front End | Made by Jonsam by ❤
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式