Apprenez la structure globale du code source axios et créez votre propre bibliothèque de requêtes

Introduction
L'article présente  axios la méthode de débogage en détail. axios L'implémentation de fonctions telles que les constructeurs, les intercepteurs et les annulations est présentée en détail  . Enfin, d'autres bibliothèques de requêtes sont comparées.

La version étudiée dans cet article est v0.19.0. Clone fork du dépôt officiel master. À partir de maintenant (14 décembre 2019), la dernière commitest 2019-12-09 15:52 ZhaoXC dc4bc49673943e352, fix: fix ignore set withCredentials false (#2582).

L'entrepôt de cet article est ici l'entrepôt github d'analyse axios de Ruochuan. Demandez-en un star.

Si vous êtes demandeur d'emploi et que le projet a été écrit et appliqué axios, l'intervieweur peut vous demander :

1. Pourquoi  axios peut-il être utilisé comme appel de fonction ou comme objet, tel que axios({}), axios.get.
2. Décrivez brièvement  axios le processus d'appel.
3. Avez-vous déjà utilisé un intercepteur ? C'est quoi le principe ?
4. Existe-t-il axiosune fonction d'annulation à utiliser ? Comment est-il atteint ?
5. Pourquoi prenez-vous en charge l'envoi de demandes dans le navigateur et prenez-vous également en charge nodel'envoi de demandes ?
Des questions comme celle-ci.

Méthode de débogage du code source axios Chrome et vscode

Il n'y a pas longtemps, l'auteur a répondu à une question sur Zhihu : que dois-je faire si le front-end ne peut pas comprendre le code source du framework front-end dans un délai d'un an ? Certains matériaux sont recommandés et le volume de lecture n'est pas mauvais.Si vous êtes intéressé, vous pouvez jeter un œil. Il y a quatre points principaux :

1. Avec l'aide du débogage
2. Recherchez et lisez les articles élogieux pertinents
3. Notez ce que vous ne comprenez pas et consultez les documents pertinents
4. Résumé

En regardant le code source, le débogage est très important, c'est pourquoi l'auteur a écrit  axios en détail la méthode de débogage du code source pour aider certains lecteurs qui ne savent peut-être pas comment déboguer.

Chrome débogue axios dans l'environnement du navigateur

méthode de débogage

axiosIl y a des fichiers après l'emballage sourcemap.

# 可以克隆笔者的这个仓库代码
git clone https://github.com/lxchuan12/axios-analysis.git
cd axios-analaysis/axios
npm install
npm start
# open [http://localhost:3000](http://localhost:3000)
# chrome F12 source 控制面板  webpack//   .  lib 目录下,根据情况自行断点调试

axios/sandbox/client.htmlCet article est débogué à travers l'exemple ci-dessus .

Au fait, permettez-moi de mentionner brièvement l' exampleexemple de débogage. Bien que cette partie ait été écrite au début de l'article, elle a été supprimée plus tard, mais je l'écris toujours après y avoir réfléchi.

Recherchez le fichier axios/examples/server.jset modifiez le code comme suit :

server = http.createServer(function (req, res) {
  var url = req.url;
  // 调试 examples
  console.log(url);
  // Process axios itself
  if (/axios\.min\.js$/.test(url)) {
    // 原来的代码 是 axios.min.js
    // pipeFileToResponse(res, '../dist/axios.min.js', 'text/javascript');
    pipeFileToResponse(res, '../dist/axios.js', 'text/javascript');
    return;
  }
  // 原来的代码 是 axios.min.map
  // if (/axios\.min.map$/.test(url)) {
  if (/axios\.map$/.test(url)) {
    // 原来的代码 是 axios.min.map
    // pipeFileToResponse(res, '../dist/axios.min.map', 'text/javascript');
    pipeFileToResponse(res, '../dist/axios.map', 'text/javascript');
    return;
  }
}
# 上述安装好依赖后
# npm run examples 不能同时开启,默认都是3000端口
# 可以指定端口 5000
# npm run examples ===  node ./examples/server.js
node ./examples/server.js -p 5000

Ouvrez http://localhost:5000, puis vous pourrez facilement déboguer les exemples Chromedans le navigateur .examples

axios Il prend en charge  node l'environnement pour envoyer des demandes. Voyons ensuite comment utiliser  vscode l'  node environnement de débogage axios.

vscode débogue axios dans l'environnement de nœud

axios-analysis/Créez un fichier dans le répertoire racine  .vscode/launch.jsoncomme suit :

{
    // 使用 IntelliSense 了解相关属性。
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Program",
            "program": "${workspaceFolder}/axios/sandbox/client.js",
            "skipFiles": [
                "<node_internals>/**"
            ]
        },
    ]
}

Appuyez simplement F5sur Démarrer le débogage, selon votre propre situation, passez au-dessus (F10), passez au (F11)débogage des points d'arrêt.

En fait, les projets open source ont généralement un guide de contribution axios/CONTRIBUTING.md, et l'auteur vient de modifier ce guide pour rendre les sourcemapfichiers référencés débogables.

Premier regard sur la structure d'axios

git clone https://github.com/lxchuan12/axios-analysis.git
cd axios-analaysis/axios
npm install
npm start

Selon la méthode de débogage mentionnée ci-dessus,  npm start enfin, déboguez directement dans  chrome le navigateur. Ouvrez http://localhost:3000 et imprimez-le sur la console axiosOn estime que de nombreuses personnes ne l'ont pas imprimé.

console.log({axios: axios});

Pour voir axios à quoi ressemble la structure couche par couche, ayez d'abord une impression générale.

L'auteur a dessiné un schéma plus détaillé.

// package.json
{
  "name": "axios",
  "version": "0.19.0",
  "description": "Promise based HTTP client for the browser and node.js",
  "main": "index.js",
  // ...
}

Après avoir lu le diagramme de structure, si vous regardez et le code source, vous constaterez qu'il est en fait similaire à la jQueryconception du code source.underscorelodashaxios

jQuery Les alias  $, underscore loadsh les alias  _ sont à la fois des fonctions et des objets. Comme jQueryl'utilisation. $('#id')$.ajax.

Ensuite, regardez l'implémentation du code source spécifique. Vous pouvez déboguer avec des points d'arrêt.

L'essentiel du débogage des points d'arrêt :
l'instruction d'affectation peut être ignorée en une seule étape, il suffit de regarder la valeur de retour et de la voir en détail plus tard.
L'exécution de la fonction doit être suivie de points d'arrêt, et elle peut également être combinée avec des commentaires et du contexte pour inverser ce que fait la fonction.

Initialisation du code source Axios

Regardez le code source La première étape est de le regarder d'abord package.json. Généralement, le fichier d'entrée principal sera déclaré  main .

// package.json
{
  "name": "axios",
  "version": "0.19.0",
  "description": "Promise based HTTP client for the browser and node.js",
  "main": "index.js",
  // ...
}

 fichier d'entrée principal

// index.js
module.exports = require('./lib/axios');

lib/axios.jsfichier principal

axios.jsIl existe relativement de nombreux codes de fichiers. Le récit est divisé en trois parties.

  1. La première partie : introduire quelques fonctions utilitaires utils, Axiosconstructeurs, configurations par défaut, defaultsetc.

  2. La deuxième partie : consiste à générer des objets d'instance  axios, axios.Axios, axios.createetc.

  3. La troisième partie annule les implémentations d'API associées, ainsi que les implémentations de all, spreadet export.

première partie

Introduisez quelques fonctions utilitaires utils, Axiosconstructeurs, configuration par défaut, defaultsetc.

// 第一部分:
// lib/axios
// 严格模式
'use strict';
// 引入 utils 对象,有很多工具方法。
var utils = require('./utils');
// 引入 bind 方法
var bind = require('./helpers/bind');
// 核心构造函数 Axios
var Axios = require('./core/Axios');
// 合并配置方法
var mergeConfig = require('./core/mergeConfig');
// 引入默认配置
var defaults = require('./defaults');

La seconde partie

est de générer des objets d'instance  axios, , axios.Axios, axios.createetc.

/**
 * Create an instance of Axios
 *
 * @param {Object} defaultConfig The default config for the instance
 * @return {Axios} A new instance of Axios
 */
function createInstance(defaultConfig) {
  // new 一个 Axios 生成实例对象
  var context = new Axios(defaultConfig);
  // bind 返回一个新的 wrap 函数,
  // 也就是为什么调用 axios 是调用 Axios.prototype.request 函数的原因
  var instance = bind(Axios.prototype.request, context);
  // Copy axios.prototype to instance
  // 复制 Axios.prototype 到实例上。
  // 也就是为什么 有 axios.get 等别名方法,
  // 且调用的是 Axios.prototype.get 等别名方法。
  utils.extend(instance, Axios.prototype, context);
  // Copy context to instance
  // 复制 context 到 intance 实例
  // 也就是为什么默认配置 axios.defaults 和拦截器  axios.interceptors 可以使用的原因
  // 其实是new Axios().defaults 和 new Axios().interceptors
  utils.extend(instance, context);
  // 最后返回实例对象,以上代码,在上文的图中都有体现。这时可以仔细看下上图。
  return instance;
}

// Create the default instance to be exported
// 导出 创建默认实例
var axios = createInstance(defaults);
// Expose Axios class to allow class inheritance
// 暴露 Axios class 允许 class 继承 也就是可以 new axios.Axios()
// 但  axios 文档中 并没有提到这个,我们平时也用得少。
axios.Axios = Axios;

// Factory for creating new instances
// 工厂模式 创建新的实例 用户可以自定义一些参数
axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};

Voici une brève description du modèle d'usine. axios.create, c'est-à-dire que l'utilisateur n'a pas besoin de savoir comment il est implémenté en interne.
Pour donner un exemple tiré de la vie, lorsque nous achetons un téléphone portable, nous n'avons pas besoin de savoir comment le téléphone portable est fabriqué, c'est le modèle d'usine.
Après avoir lu la deuxième partie, plusieurs fonctions d'outil sont impliquées, telles que bind, extend. Ces outils et méthodes sont décrits ci-après.

Bind de la méthode de l'outil

axios/lib/helpers/bind.js

'use strict';
// 返回一个新的函数 wrap
module.exports = function bind(fn, thisArg) {
  returnfunction wrap() {
    var args = newArray(arguments.length);
    for (var i = 0; i < args.length; i++) {
      args[i] = arguments[i];
    }
    // 把 argument 对象放在数组 args 里
    return fn.apply(thisArg, args);
  };
};

Passez deux paramètres fonction et thisArgpointeur.
Générez un tableau de parameters argumentset appelez enfin pour renvoyer la structure du paramètre. En fait , ces objets de type tableau sont
désormais  apply pris en charge  et il n'est pas nécessaire de convertir manuellement le tableau. Alors pourquoi l'auteur veut-il convertir le tableau, pour des raisons de performances ? Pas pris en charge à l'époque ? Ou est-ce que l'auteur ne le sait pas ? Ceci est inconnu. Les lecteurs qui connaissent sont invités à le dire à l'auteur dans la zone de commentaires.arguments

Pour les lecteurs qui ne sont pas très familiers avec , , et etc., vous pouvez lire un autre article de applyl' callauteur . L'intervieweur a demandé : Pouvez-vous simuler la méthode de liaison de JSbind面试官问系列

Par exemple

function fn(){
  console.log.apply(console, arguments);
}
fn(1,2,3,4,5,6, '若川');
// 1 2 3 4 5 6 '若川'

utils.extend de la méthode outil

axios/lib/utils.js

function extend(a, b, thisArg) {
  forEach(b, function assignValue(val, key) {
    if (thisArg && typeof val === 'function') {
      a[key] = bind(val, thisArg);
    } else {
      a[key] = val;
    }
  });
  return a;
}

En fait, il s'agit de parcourir l'  b objet paramètre, de le copier dans  a l'objet et de l'appeler s'il s'agit d'une fonction  bind .

utils.forEach de la méthode outil

axios/lib/utils.js

Itérer sur des tableaux et des objets. Le modèle de conception est appelé modèle itérateur. De nombreux codes sources ont des fonctions de traversée comme celle-ci. Comme nous le savons tous jQuery $.each.

/**
 * @param {Object|Array} obj The object to iterate
 * @param {Function} fn The callback to invoke for each item
 */
function forEach(obj, fn) {
  // Don't bother if no value provided
  // 判断 null 和 undefined 直接返回
  if (obj === null || typeof obj === 'undefined') {
    return;
  }

  // Force an array if not already something iterable
  // 如果不是对象,放在数组里。
  if (typeof obj !== 'object') {
    /*eslint no-param-reassign:0*/
    obj = [obj];
  }

  // 是数组 则用for 循环,调用 fn 函数。参数类似 Array.prototype.forEach 的前三个参数。
  if (isArray(obj)) {
    // Iterate over array values
    for (var i = 0, l = obj.length; i < l; i++) {
      fn.call(null, obj[i], i, obj);
    }
  } else {
    // Iterate over object keys
    // 用 for in 遍历对象,但 for in 会遍历原型链上可遍历的属性。
    // 所以用 hasOwnProperty 来过滤自身属性了。
    // 其实也可以用Object.keys来遍历,它不遍历原型链上可遍历的属性。
    for (var key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        fn.call(null, obj[key], key, obj);
      }
    }
  }
}

Si vous n'êtes pas familier avec Objectles informations pertinentes API, vous pouvez consulter un article que j'ai écrit auparavant. JavaScript Object All API Parsing

la troisième partie

Annulez les implémentations d'API associées, ainsi que les implémentations de all, spreadet export.

// Expose Cancel & CancelToken
// 导出 Cancel 和 CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');

// Expose all/spread
// 导出 all 和 spread API
axios.all = function all(promises) {
  returnPromise.all(promises);
};
axios.spread = require('./helpers/spread');

module.exports = axios;

// Allow use of default import syntax in TypeScript
// 也就是可以以下方式引入
// import axios from 'axios';
module.exports.default = axios;

Introduits ici  spread, ceux annulés APIne seront pas analysés pour le moment, et seront analysés en détail ultérieurement.

Supposons que vous ayez un tel besoin.

function f(x, y, z) {}
var args = [1, 2, 3];
f.apply(null, args);

Ensuite, vous pouvez utiliser spreadla méthode. usage:

axios.spread(function(x, y, z) {})([1, 2, 3]);
/**
 * @param {Function} callback
 * @returns {Function}
 */
module.exports = function spread(callback) {
  returnfunction wrap(arr) {
    return callback.apply(null, arr);
  };
};

Ci-dessus var context = new Axios(defaultConfig);, le constructeur principal est présenté ensuite Axios.

Axios du constructeur principal

axios/lib/core/Axios.js

constructeur Axios.

function Axios(instanceConfig) {
  // 默认参数
  this.defaults = instanceConfig;
  // 拦截器 请求和响应拦截器
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}
Axios.prototype.request = function(config){
  // 省略,这个是核心方法,后文结合例子详细描述
  // code ...
  var promise = Promise.resolve(config);
  // code ...
  return promise;
}
// 这是获取 Uri 的函数,这里省略
Axios.prototype.getUri = function(){}
// 提供一些请求方法的别名
// Provide aliases for supported request methods
// 遍历执行
// 也就是为啥我们可以 axios.get 等别名的方式调用,而且调用的是 Axios.prototype.request 方法
// 这个也在上面的 axios 结构图上有所体现。
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    returnthis.request(utils.merge(config || {}, {
      method: method,
      url: url
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    returnthis.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

module.exports = Axios;

Regardez ensuite la section de l'intercepteur.

Constructeur de gestion d'intercepteur InterceptorManager

Intercepter avant la demande et intercepter après la demande.
Il est utilisé dans Axios.prototype.requestla fonction, et la façon de mettre en œuvre l'interception sera décrite en détail plus tard avec des exemples.

documentation de l'intercepteur de référentiel axios github

comment utiliser:

// Add a request interceptor
// 添加请求前拦截器
axios.interceptors.request.use(function (config) {
  // Do something before request is sent
  return config;
}, function (error) {
  // Do something with request error
  returnPromise.reject(error);
});

// Add a response interceptor
// 添加请求后拦截器
axios.interceptors.response.use(function (response) {
  // Any status code that lie within the range of 2xx cause this function to trigger
  // Do something with response data
  return response;
}, function (error) {
  // Any status codes that falls outside the range of 2xx cause this function to trigger
  // Do something with response error
  returnPromise.reject(error);
});

Si vous voulez mettre l'intercepteur, vous pouvez utiliser ejectla méthode.

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

Des intercepteurs peuvent également être ajoutés sur des instances personnalisées.

const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});

Implémentation source :

Constructeur handles pour stocker les fonctions d'intercepteur.

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

Ensuite, trois méthodes sont déclarées : use, remove et traverse.

Utilisation d'InterceptorManager.prototype.use

Passez deux fonctions en paramètres et stockez un élément dans le tableau {fulfilled: function(){}, rejected: function(){}}. Renvoie un nombre  ID, utilisé pour supprimer l'intercepteur.

/**
 * @param {Function} fulfilled The function to handle `then` for a `Promise`
 * @param {Function} rejected The function to handle `reject` for a `Promise`
 *
 * @return {Number} 返回ID 是为了用 eject 移除
 */
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  returnthis.handlers.length - 1;
};

InterceptorManager.prototype.eject supprimé

 Supprimer l'intercepteur en fonction  use du retour ID

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

Un peu similaire à timer sum setTimeout ,  setIntervalla valeur de retour est id. Utilisez clearTimeout et clearIntervalpour effacer la minuterie.

// 提一下 定时器回调函数是可以传参的,返回值 timer 是数字
var timer = setInterval((name) => {
  console.log(name);
}, 1000, '若川');
console.log(timer); // 数字 ID
// 在控制台等会再输入执行这句,定时器就被清除了
clearInterval(timer);

InterceptorManager.prototype.forEach Traversal

Parcourez et exécutez tous les intercepteurs, passez un appel de fonction de rappel (chaque fonction d'intercepteur en tant que paramètre), l'élément qui est supprimé l'est, nullil ne sera donc pas exécuté et l'effet de la suppression est atteint.

/**
 * @param {Function} fn The function to call for each interceptor
 */
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
};

liaison d'instance

L'opération de débogage décrite ci-dessus npm start utilise axios/sandbox/client.htmlle fichier de chemin comme exemple, et les lecteurs peuvent déboguer eux-mêmes.

Vous trouverez ci-dessous un morceau de code dans ce fichier.

axios(options)
.then(function (res) {
  response.innerHTML = JSON.stringify(res.data, null, 2);
})
.catch(function (res) {
  response.innerHTML = JSON.stringify(res.data, null, 2);
});

Examinez d'abord le processus de pile d'appels

Si vous ne voulez pas déboguer étape par étape, il existe un moyen délicat.
Je sais que  axios je l'ai utilisé XMLHttpRequest.
Peut être recherché dans le projet : new XMLHttpRequest.
Localisez le fichier   et définissez un point d'arrêt dans le navigateur pour déboguer axios/lib/adapters/xhr.js
cette instruction  , puis examinez l'implémentation de fonctions spécifiques en fonction de la pile d'appels.var request = new XMLHttpRequest();
chrome

Call Stack

dispatchXhrRequest (xhr.js:19)
xhrAdapter (xhr.js:12)
dispatchRequest (dispatchRequest.js:60)
Promise.then (async)
request (Axios.js:54)
wrap (bind.js:10)
submit.onclick ((index):138)

Décrivez brièvement le processus :

  1. Send Request clic sur le bouton submit.onclick

  2. Appeler  axios une fonction revient en fait à appeler  Axios.prototype.request une fonction, et cette fonction utilise  bind une fonction renvoyée wrapnommée .

  3. transfert Axios.prototype.request

  4. (Exécuter l'intercepteur de requête lorsqu'il y a un intercepteur de requête), et  dispatchRequestla méthode sera exécutée au milieu

  5. dispatchRequest après avoir appelé adapter (xhrAdapter)

  6. Promise La fonction dans le dernier appel  dispatchXhrRequest, (s'il y a un intercepteur de réponse, l'intercepteur de réponse sera appelé à la fin)

Si vous lisez attentivement le début de l'article axios 结构关系图, vous avez en fait une compréhension générale de ce processus.

Ensuite, regardons  Axios.prototype.request l'implémentation spécifique.

Méthode principale de requête Axios.prototype.request

Cette fonction est la fonction principale. A principalement fait les choses suivantes :

1. Si le premier paramètre est jugé être une chaîne, définissez l'url, c'est-à-dire support axios('example/url', [, config])et également support axios({}).
2. Combinez les paramètres par défaut et les paramètres passés par l'utilisateur.
3. Définissez la méthode de la requête. La valeur par défaut est getla méthode
. 4. Combinez les intercepteurs de requête et de réponse définis par l'utilisateur, et envoyez la requête pour former une dispatchRequestchaîne. Promise, et enfin renvoyer Promisel'instance.

C'est-à-dire qu'il est garanti que l'intercepteur est exécuté avant la requête, puis la requête est envoyée, et l'intercepteur est exécuté en réponse à cet ordre. <br>
C'est pourquoi les méthodes `then` et `catch` sont toujours disponibles à la fin. <br>

Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  // 这一段代码 其实就是 使 axios('example/url', [, config])
  // config 参数可以省略
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  // 合并默认参数和用户传递的参数
  config = mergeConfig(this.defaults, config);

  // Set config.method
  // 设置 请求方法,默认 get 。
  if (config.method) {
    config.method = config.method.toLowerCase();
  } elseif (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }
  // Hook up interceptors middleware
  // 组成`Promise`链 这段拆开到后文再讲述
};

 Former Promiseune chaîne et renvoyer Promiseune instance

Cette partie : les intercepteurs de requêtes et de réponses définis par l'utilisateur, et dispatchRequestla Promisechaîne de composition des requêtes envoyées. C'est-à-dire qu'il est garanti que l'intercepteur s'exécute avant la demande, puis envoie la demande, puis répond à l'intercepteur pour exécuter cet ordre

也就是保证了请求前拦截器先执行,然后发送请求,再响应拦截器执行这样的顺序<br>
也就是为啥最后还是可以`then`,`catch`方法的缘故。<br>

Si les lecteurs Promisene le connaissent pas, il est recommandé de lire le livre de M. Ruan "Introduction aux normes ES6". Mini-livre ES6 Promise-resolve et JavaScript Promise de Ruan Yifeng (version chinoise)

// 组成`Promise`链
  // Hook up interceptors middleware
  // 把 xhr 请求 的 dispatchRequest 和 undefined 放在一个数组里
  var chain = [dispatchRequest, undefined];
  // 创建 Promise 实例
  var promise = Promise.resolve(config);

 // 遍历用户设置的请求拦截器 放到数组的 chain 前面
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

 // 遍历用户设置的响应拦截器 放到数组的 chain 后面
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

 // 遍历 chain 数组,直到遍历 chain.length 为 0
  while (chain.length) {
    // 两两对应移出来 放到 then 的两个参数里。
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
var promise = Promise.resolve(config);

Expliquez cette phrase. Le rôle est de générer Promisedes instances.

var promise = Promise.resolve({name: '若川'})
// 等价于
// new Promise(resolve => resolve({name: '若川'}))
promise.then(function (config){
  console.log(config)
});
// {name: "若川"}

Expliquez également ce qui apparaîtra plus tardPromise.reject(error); :

Promise.reject(error);
var promise = Promise.reject({name: '若川'})
// 等价于
// new Promise(reject => reject({name: '若川'}))

// promise.then(null, function (config){
//   console.log(config)
// });
// 等价于
promise.catch(function (config){
  console.log(config)
});
// {name: "若川"}

Comprenons ce code avec un exemple.
Malheureusement, exampleil n'y a pas d'exemples d'intercepteurs dans le dossier. L'auteur a ajouté un exemple d'intercepteur sur la base de exampleZhongji . , ce qui est pratique pour les lecteurs à déboguer.example/getaxios/examples/interceptors

node ./examples/server.js -p 5000

promise = promise.then(chain.shift(), chain.shift());Ce code atteint un point d'arrêt.

Vous obtiendrez une image comme celle-ci.

localPortez une attention particulière au tableau en bas, à droite chain. C'est la structure.

var chain = [
  '请求成功拦截2', '请求失败拦截2',
  '请求成功拦截1', '请求失败拦截1',
  dispatch,  undefined,
  '响应成功拦截1', '响应失败拦截1',
  '响应成功拦截2', '响应失败拦截2',
]

 Ce code est relativement compliqué. Autrement dit, un code similaire au suivant sera généré et dispatchRequestla méthode sera appelée au milieu

// config 是 用户配置和默认配置合并的
var promise = Promise.resolve(config);
promise.then('请求成功拦截2', '请求失败拦截2')
.then('请求成功拦截1', '请求失败拦截1')
.then(dispatchRequest, undefined)
.then('响应成功拦截1', '响应失败拦截1')
.then('响应成功拦截2', '响应失败拦截2')

.then('用户写的业务处理函数')
.catch('用户写的报错业务处理函数');

Voici promise thenla catchconnaissance :
Promise.prototype.thenle premier paramètre de la méthode est resolvedla fonction de rappel de l'état, et le deuxième paramètre (facultatif) est rejectedla fonction de rappel de l'état. Il vient donc par paire.
Promise.prototype.catchmethod est un alias pour .then(null, rejection)ou .then(undefined, rejection)qui spécifie une fonction de rappel lorsqu'une erreur se produit.
thenLa méthode renvoie une nouvelle Promiseinstance (notez, pas l' Promiseinstance d'origine). Par conséquent, l'écriture en chaîne peut être utilisée, c'est-à-dire qu'une thenautre méthode est appelée après la méthode then.

Combiné avec l'exemple ci-dessus plus en détail, le code est comme ceci.

var promise = Promise.resolve(config);
// promise.then('请求成功拦截2', '请求失败拦截2')
promise.then(function requestSuccess2(config) {
  console.log('------request------success------2');
  return config;
}, function requestError2(error) {
  console.log('------response------error------2');
  returnPromise.reject(error);
})

// .then('请求成功拦截1', '请求失败拦截1')
.then(function requestSuccess1(config) {
  console.log('------request------success------1');
  return config;
}, function requestError1(error) {
  console.log('------response------error------1');
  returnPromise.reject(error);
})

// .then(dispatchRequest, undefined)
.then( function dispatchRequest(config) {
  /**
    * 适配器返回的也是Promise 实例
      adapter = function xhrAdapter(config) {
            return new Promise(function dispatchXhrRequest(resolve, reject) {})
      }
    **/
  return adapter(config).then(function onAdapterResolution(response) {
    // 省略代码 ...
    return response;
  }, function onAdapterRejection(reason) {
    // 省略代码 ...
    returnPromise.reject(reason);
  });
}, undefined)

// .then('响应成功拦截1', '响应失败拦截1')
.then(function responseSuccess1(response) {
  console.log('------response------success------1');
  return response;
}, function responseError1(error) {
  console.log('------response------error------1');
  returnPromise.reject(error);
})

// .then('响应成功拦截2', '响应失败拦截2')
.then(function responseSuccess2(response) {
  console.log('------response------success------2');
  return response;
}, function responseError2(error) {
  console.log('------response------error------2');
  returnPromise.reject(error);
})

// .then('用户写的业务处理函数')
// .catch('用户写的报错业务处理函数');
.then(function (response) {
  console.log('哈哈哈,终于获取到数据了', response);
})
.catch(function (err) {
  console.log('哎呀,怎么报错了', err);
});

Regardez attentivement cet Promiseappel en chaîne, le code est similaire. thenLe dernier paramètre renvoyé par la méthode est thenle premier paramètre de la méthode suivante.
catchToutes les captures d'erreurs sont renvoyées Promise.reject(error), ce qui permet catchaux utilisateurs de détecter les erreurs.

Par exemple:

var p1 = newPromise((resolve, reject) => {
 reject(newError({name: '若川'}));
});

p1.catch(err => {
    console.log(res, 'err');
    returnPromise.reject(err)
})
.catch(err => {
 console.log(err, 'err1');
})
.catch(err => {
 console.log(err, 'err2');
});

err2Il ne sera pas capturé, c'est-à-dire qu'il ne sera pas exécuté, mais s'il renvoie return Promise.reject(err), il peut être capturé.

Enfin, faites un dessin pour résumer  Promise l'appel en chaîne.

Résumé : 1. Les intercepteurs de requêtes et de réponses peuvent être écrits Promise.

  1. Si plusieurs répondeurs de demande sont définis, celui défini plus tard sera exécuté en premier.

  2. Si plusieurs intercepteurs de réponse sont définis, celui défini en premier sera exécuté en premier.

dispatchRequest(config) Voici configce que renvoie l'intercepteur de réussite de la demande. Regardez ensuite dispatchRequestla fonction.

dispatchRequest demande d'expédition finale

Cette fonction fait principalement les choses suivantes :

1. S'il a été annulé,  throw la raison sera signalée comme une erreur et la direction sera Promisemodifiée rejected.
2. Assurer  config.header l'existence.
3. Convertissez les données à l'aide de convertisseurs de requêtes définis par l'utilisateur et par défaut.
4. Frappez à plat  config.header.
5. Supprimez quelques fichiers  config.header.
6. Renvoyez l'instance après l'exécution de l'adaptateur adapter( Promiseinstance)  thenaprès l'exécution  Promise. Le résultat renvoyé est transmis à l'intercepteur de réponse pour traitement.

 

'use strict';
// utils 工具函数
var utils = require('./../utils');
// 转换数据
var transformData = require('./transformData');
// 取消状态
var isCancel = require('../cancel/isCancel');
// 默认参数
var defaults = require('../defaults');

/**
 * 抛出 错误原因,使`Promise`走向`rejected`
 */
function throwIfCancellationRequested(config) {
  if (config.cancelToken) {
    config.cancelToken.throwIfRequested();
  }
}

/**
 * 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) {
  // 取消相关
  throwIfCancellationRequested(config);

  // Ensure headers exist
  // 确保 headers 存在
  config.headers = config.headers || {};

  // Transform request data
  // 转换请求的数据
  config.data = transformData(
    config.data,
    config.headers,
    config.transformRequest
  );

  // Flatten headers
  // 拍平 headers
  config.headers = utils.merge(
    config.headers.common || {},
    config.headers[config.method] || {},
    config.headers || {}
  );

  // 以下这些方法 删除 headers
  utils.forEach(
    ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
    function cleanHeaderConfig(method) {
      delete config.headers[method];
    }
  );
  // adapter 适配器部分 拆开 放在下文讲
};

transformData de dispatchRequest transforme les données

Il y a une fonction dans le code ci-dessus  transformData , qui est expliquée ici. En fait, il parcourt le tableau de fonctions transmis pour opérer sur les données et renvoie finalement les données.

axios.defaults.transformResponse Il y a une fonction dans le tableau par défaut, utilisez donc concatla fonction personnalisée liée.

utiliser:

chemin du fichieraxios/examples/transform-response/index.html

Ce code convertit en fait une chaîne au format horaire en un objet horaire, qui peut être appelé directement getMonth.

var ISO_8601 = /(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})Z/;
function formatDate(d) {
  return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear();
}

axios.get('https://api.github.com/users/mzabriskie', {
  transformResponse: axios.defaults.transformResponse.concat(function (data, headers) {
    Object.keys(data).forEach(function (k) {
      if (ISO_8601.test(data[k])) {
        data[k] = newDate(Date.parse(data[k]));
      }
    });
    return data;
  })
})
.then(function (res) {
  document.getElementById('created').innerHTML = formatDate(res.data.created_at);
});

code source:

 Il s'agit de parcourir le tableau, d'appeler la fonction de transfert data et  d'appel de paramètre dans le tableau  .headers

module.exports = function transformData(data, headers, fns) {
  /*eslint no-param-reassign:0*/
  utils.forEach(fns, function transform(fn) {
    data = fn(data, headers);
  });

  return data;
};

La partie exécution de l'adaptateur adaptateur de dispatchRequest

L'adaptateur est appelé modèle d'adaptateur dans le modèle de conception. Laissez-moi vous donner un exemple simple dans la vie, il sera facile pour tout le monde de comprendre.

Nous utilisions souvent des trous ronds pour les prises casque des téléphones portables auparavant, mais maintenant la prise casque et l'interface de charge sont essentiellement combinées en une seule. unifié comme typec.

Pour le moment, nous en avons besoin d'un typec转圆孔的转接口, qui est l'adaptateur.

// adapter 适配器部分
  var adapter = config.adapter || defaults.adapter;

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

    // Transform response data
    // 转换响应的数据
    response.data = transformData(
      response.data,
      response.headers,
      config.transformResponse
    );

    return response;
  }, function onAdapterRejection(reason) {
    if (!isCancel(reason)) {
      // 取消相关
      throwIfCancellationRequested(config);

      // Transform response data
      // 转换响应的数据
      if (reason && reason.response) {
        reason.response.data = transformData(
          reason.response.data,
          reason.response.headers,
          config.transformResponse
        );
      }
    }

    returnPromise.reject(reason);
  });

Regardons les détails ensuite  adapter.

L'adaptateur adaptateur envoie réellement la demande

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

Après avoir lu ce qui précède  adapter, vous pouvez savoir que la personnalisation de l'utilisateur est prise en charge. wx.request Par exemple, vous pouvez en écrire un selon vos besoins  via l'applet WeChat  adapter.
Jetons un coup d'oeil  defaults.ddapter.
chemin du fichier:axios/lib/defaults.js

Importer selon l'environnement courant, s'il est importé dans l'environnement du navigateur xhr, il est nodeimporté dans l'environnement http.
Un nodeenvironnement de jugement similaire sentry-javascriptpeut également être vu dans le code source.

function getDefaultAdapter() {
  var adapter;
  // 根据 XMLHttpRequest 判断
  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
    // 根据 process 判断
  } elseif (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  }
  return adapter;
}
var defaults = {
  adapter: getDefaultAdapter(),
  // ...
};

xhr

Viennent ensuite  XMLHttpRequest les objets qui nous sont familiers.

Les lecteurs qui ne comprennent pas peuvent se référer au document XMLHttpRequest MDN.

Le rappel principal : onabortil s'agit d'un événement d'annulation de demande, withCredentialsqui est une valeur booléenne utilisée pour spécifier  Access-Control si la demande interdomaine doit avoir des informations d'autorisation, telles que  cookie ou  header en-tête d'autorisation.

Ce morceau de code a été supprimé, vous pouvez vous référer à axios-analysisl'entrepôt de Ruochuan pour plus de détails, ou vous pouvez cloner l' axios-analysisentrepôt de l'auteur puis l'analyser en détail lors du débogage.

module.exports = function xhrAdapter(config) {
  returnnewPromise(function dispatchXhrRequest(resolve, reject) {
    // 这块代码有删减
    var request = new XMLHttpRequest();
    request.open()
    request.timeout = config.timeout;
    // 监听 state 改变
    request.onreadystatechange = function handleLoad() {
      if (!request || request.readyState !== 4) {
        return;
      }
      // ...
    }
    // 取消
    request.onabort = function(){};
    // 错误
    request.onerror = function(){};
    // 超时
    request.ontimeout = function(){};
    // cookies 跨域携带 cookies 面试官常喜欢考这个
    // 一个布尔值,用来指定跨域 Access-Control 请求是否应带有授权信息,如 cookie 或授权 header 头。
    // Add withCredentials to request if needed
    if (!utils.isUndefined(config.withCredentials)) {
      request.withCredentials = !!config.withCredentials;
    }

    // 上传下载进度相关
    // Handle progress if needed
    if (typeof config.onDownloadProgress === 'function') {
      request.addEventListener('progress', config.onDownloadProgress);
    }

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

    // Send the request
    // 发送请求
    request.send(requestData);
  });
}

En fait  fetch , le support est très bon maintenant. La bibliothèque de requêtes open source umi-request d'Ali est empaquetée fetchau lieu d'être utilisée XMLHttpRequest. À la fin de l'article, la différence entre le suivant  umi-request et  le axios suivant est grossièrement décrite.

http

httpJe n'entrerai pas dans les détails ici, les lecteurs intéressés peuvent vérifier axios-analysispar eux-mêmes l'entrepôt de Ruochuan.

module.exports = function httpAdapter(config) {
  returnnewPromise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
  });
};

Il y a un module d'annulation ci-dessus  dispatchRequest , qui je pense est le point clé, alors parlons-en en détail à la fin :

Module d'annulation de dispatchRequest

Les demandes d'annulation sont disponibles cancel token.

L'API de jeton d'annulation axios est une  promise proposition d'annulation basée sur la révocation.

L'API de jeton d'annulation axios est basée sur la proposition de promesses annulables retirées.

annulation de document axios

Les deux modes d'utilisation sont décrits en détail dans la documentation.

Malheureusement, exampleil n'y a pas d'exemple d'annulation dans le dossier. L'auteur a ajouté un exemple d'annulation sur la base de exampleZhongji . , ce qui est pratique pour les lecteurs à déboguer.example/getaxios/examples/cancel

node ./examples/server.js -p 5000

requestLes deux modules de l'intercepteur en et dispatchde l'annulation en sont relativement complexes, et peuvent être débogués et absorbés.

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/get/server', {
  cancelToken: source.token
}).catch(function (err) {
  if (axios.isCancel(err)) {
    console.log('Request canceled', err.message);
  } else {
    // handle error
  }
});

// cancel the request (the message parameter is optional)
// 取消函数。
source.cancel('哎呀,我被若川取消了');

Exemple de code de module de demande d'annulation

Combiné avec le processus d'annulation du code source, c'est probablement le cas. Cette section est placée dans le code axios/examples/cancel-token/index.html.

Le paramètre  config.cancelToken est généré source.cancel('哎呀,我被若川取消了');uniquement lorsqu'il est déclenché.

// source.cancel('哎呀,我被若川取消了');
// 点击取消时才会 生成 cancelToken 实例对象。
// 点击取消后,会生成原因,看懂了这段在看之后的源码,可能就好理解了。
var config = {
  name: '若川',
  // 这里简化了
  cancelToken: {
        promise: newPromise(function(resolve){
            resolve({ message: '哎呀,我被若川取消了'})
        }),
        reason: { message: '哎呀,我被若川取消了' }
  },
};
// 取消 抛出异常方法
function throwIfCancellationRequested(config){
  // 取消的情况下执行这句
  if(config.cancelToken){
    //   这里源代码 便于执行,我改成具体代码
    // config.cancelToken.throwIfRequested();
    // if (this.reason) {
    //     throw this.reason;
    //   }
    if(config.cancelToken.reason){
        throw config.cancelToken.reason;
    }
  }
}

function dispatchRequest(config){
  // 有可能是执行到这里就取消了,所以抛出错误会被err2 捕获到
  throwIfCancellationRequested(config);
  //  adapter xhr适配器
  returnnewPromise((resovle, reject) => {
    var request = new XMLHttpRequest();
    console.log('request', request);
    if (config.cancelToken) {
        // Handle cancellation
        config.cancelToken.promise.then(function onCanceled(cancel) {
            if (!request) {
                return;
            }

            request.abort();
            reject(cancel);
            // Clean up request
            request = null;
        });
    }
  })
  .then(function(res){
    // 有可能是执行到这里就才取消 取消的情况下执行这句
    throwIfCancellationRequested(config);
    console.log('res', res);
    return res;
  })
  .catch(function(reason){
    // 有可能是执行到这里就才取消 取消的情况下执行这句
    throwIfCancellationRequested(config);
    console.log('reason', reason);
    returnPromise.reject(reason);
  });
}

var promise = Promise.resolve(config);

// 没设置拦截器的情况下是这样的
promise
.then(dispatchRequest, undefined)
// 用户定义的then 和 catch
.then(function(res){
  console.log('res1', res);
  return res;
})
.catch(function(err){
  console.log('err2', err);
  returnPromise.reject(err);
});
// err2 {message: "哎呀,我被若川取消了"}

Ensuite, regardez le code source du module d'annulation

Voir comment passer generate config.cancelToken.

chemin du fichier:

axios/lib/cancel/CancelToken.js

const CancelToken = axios.CancelToken;
const source = CancelToken.source();
source.cancel('哎呀,我被若川取消了');

Mise en œuvre vue par l'exemple  CancelToken.source,

CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  // token
  return {
    token: token,
    cancel: cancel
  };
};

La structure générale après exécution sourceest comme ceci.

{
    token: {
    promise: newPromise(function(resolve){
      resolve({ message: '哎呀,我被若川取消了'})
    }),
    reason: { message: '哎呀,我被若川取消了' }
  },
  cancel: function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      // 已经取消
      return;
    }
    token.reason = {message: '哎呀,我被若川取消了'};
  }
}

continuer de regarder new CancelToken

// CancelToken
// 通过 CancelToken 来取消请求操作
function CancelToken(executor) {
  if (typeof executor !== 'function') {
    thrownewTypeError('executor must be a function.');
  }

  var resolvePromise;
  this.promise = newPromise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  var token = this;
  executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      // 已经取消
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

module.exports = CancelToken;

Ceci est utilisé dans l'adaptateur qui envoie la demande.

// xhr
if (config.cancelToken) {
  // Handle cancellation
  config.cancelToken.promise.then(function onCanceled(cancel) {
    if (!request) {
      return;
    }

    request.abort();
    reject(cancel);
    // Clean up request
    request = null;
  });
}

dispatchRequest L'implémentation spécifique dans throwIfCancellationRequested: throw lève une exception.

// 抛出异常函数
function throwIfCancellationRequested(config) {
  if (config.cancelToken) {
    config.cancelToken.throwIfRequested();
  }
}
// 抛出异常 用户 { message: '哎呀,我被若川取消了' }
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
  if (this.reason) {
    throwthis.reason;
  }
};

Annuler la pile d'appels de processus

1.source.cancel()
2.resolvePromise(token.reason);
3.config.cancelToken.promise.then(function onCanceled(cancel) {})

dernière entréerequest.abort();``reject(cancel);

Le processus d'annulation est présenté ici. L'essentiel est de passer les paramètres de configuration cancelToken, il sera généré lors de son annulation cancelToken, et s'il est jugé, une erreur sera levée, Promise afin rejectedque l'utilisateur puisse capturer le message {message : 'Informations d'annulation définies par l'utilisateur' }.

L'article est écrit ici et il approche essentiellement de la fin.

Si vous savez lire jusqu'au bout, c'est que vous avez surpassé beaucoup de gens ^_^

axiosC'est une très bonne bibliothèque de requêtes, mais elle ne peut certainement pas répondre aux besoins de tous les développeurs. Ensuite, comparez d'autres bibliothèques pour voir quels sont les besoins spécifiques des autres développeurs.

Comparé à d'autres bibliothèques de requêtes

Ajax

Shuige, responsable de la communauté FCC Chengdu, a ouvert KoAJAX.

Comment tenir une conférence technique avec un logiciel open source ? Un extrait de l'article ci-dessous.

Bibliothèque de requêtes front-end - KoAJAX La bibliothèque de requêtes HTTP la plus couramment utilisée pour les étudiants front-end nationaux devrait être axios, n'est-ce pas ? Bien que son API Interceptor (intercepteur) soit .use(), il est complètement différent du mode middleware de Node.js Express, Koa et d'autres frameworks.Par rapport à jQuery .ajaxPrefilter() et dataFilter(), il n'y a pas d'amélioration substantielle ; upload , La progression du téléchargement est plus simple que jQuery.Deferred(), juste deux options de rappel spéciales. Par conséquent, il doit encore mémoriser des API spécifiques pour des besoins spécifiques, ce qui n'est pas assez concis.

Heureusement, dans le processus de recherche sur l'utilisation des itérateurs asynchrones ES 2018 pour implémenter un moteur middleware de type Koa, Shuige a créé une application de niveau supérieur plus pratique - KoAJAX. L'ensemble de son processus d'exécution est basé sur un middleware de style Koa, et il s'agit lui-même d'une pile d'appels middleware. En plus des méthodes de raccourci telles que .get(), .post(), .put(), .delete() couramment utilisées dans l'API RESTful, les développeurs n'ont qu'à se souvenir de .use() et next(), et les autres sont des normes ES Grammaire et déduction de type TS.

bibliothèque de requêtes open source Ali umi-request

dépôt github umi-request

umi-request Et  fetchaxios similitudes et différences.

Je dois dire qu'il umi-request est vraiment puissant et que les lecteurs intéressés peuvent lire son code source.

axiosSur la base de la compréhension , umi-requestil ne devrait pas être difficile de comprendre le code source.

Par exemple,  umi-request le code du module d'annulation est presque exactement axiosle même que .

Résumer

L'article décrit  axios la méthode de débogage en détail. axios L'implémentation de fonctions telles que les constructeurs, les intercepteurs et les annulations est présentée en détail  . Enfin, d'autres bibliothèques de requêtes sont comparées.

Enfin, faites un dessin pour résumer le processus global d'axios.

Répondez aux questions posées au début de l'article :

Si vous êtes demandeur d'emploi et que le projet a été écrit et appliqué axios, l'intervieweur peut vous demander :

1. Pourquoi  axios peut-il être utilisé comme appel de fonction ou comme objet, tel que axios({}), axios.get.
Réponse : axiosL'essence est une fonction, et certaines méthodes d'alias sont assignées, telles que get, postméthodes, qui peuvent être appelées, et l'appel final est toujours Axios.prototype.requestune fonction.
2. Décrivez brièvement  axios le processus d'appel.
Réponse : Il s'agit en fait de la méthode appelée Axios.prototype.request, et le retour final est promiseun appel en chaîne, et la demande réelle est dispatchRequestenvoyée dans .
3. Avez-vous déjà utilisé un intercepteur ? C'est quoi le principe ?
Réponse : Oui, utilisez axios.interceptors.request.usela fonction d'interception de réussite et d'échec de la demande d'ajout, utilisez axios.interceptors.response.usela fonction d'interception de réussite et d'échec de la réponse d'ajout. Lorsque Axios.prototype.requestles fonctions forment promisedes appels en chaîne, Interceptors.protype.forEachdes intercepteurs de demande de traversée et de réponse sont ajoutés aux dispatchRequestdeux extrémités de la demande réelle, de manière à réaliser une interception avant la demande et une interception après la réponse. Les intercepteurs prennent également en charge Interceptors.protype.ejectla suppression de méthode.
4. Existe-t-il axiosune fonction d'annulation à utiliser ? Comment est-il atteint ? Réponse : Il a été utilisé et peut être annulé
en passant configla configuration . cancelTokenIl est jugé qu'il y a une erreur générée cancelTokendans promisel'appel de chaîne et la demande est annulée au milieu, de sorte que les informations d'annulation sont capturées par l'utilisateur. 5. Pourquoi prenez-vous en charge l'envoi de demandes dans le navigateur et prenez-vous également en charge l'envoi de demandes ? répondre:dispatchRequestadapterrequest.abort()promiserejected
node
axios.defaults.adapterDans la configuration par défaut, on détermine s'il s'agit d'un navigateur ou noded'un environnement selon l'environnement, et l'adaptateur correspondant est utilisé. Les adaptateurs prennent en charge la personnalisation.

Pour répondre aux questions de l'intervieweur, les lecteurs peuvent également organiser le langage selon leur propre compréhension. La réponse de l'auteur est juste pour référence.

axios Le code source est relativement petit, plus d'un millier de lignes après l'emballage, il est relativement facile à lire et il vaut vraiment la peine d'être appris.

Il est suggéré  clone que l'entrepôt github d'analyse axios de Ruochuan soit débogué selon la méthode de l'article, ce qui sera plus impressionnant.

En fonction de Promisela composition de Promisela chaîne, configurez intelligemment l'interception des demandes, envoyez la demande, puis essayez de répondre à l'intercepteur.

requestLes deux modules de l'intercepteur en et dispatchde l'annulation en sont relativement complexes, et peuvent être débogués et absorbés.

axios C'est une fonction, et quand c'est une fonction, elle appelle une fonction, et c'est un objet. Il y a des méthodes de requête telles que , etc. Axios.prototype.requestdessus , et finalement une fonction est appelée.getpostAxios.prototype.request

axios De nombreux modèles de conception sont utilisés dans le code source. Tels que le modèle d'usine, le modèle d'itérateur, le modèle d'adaptateur, etc. Si vous souhaitez apprendre les modèles de conception de manière systématique, il est généralement recommandé de recommander les modèles de conception et les pratiques de développement JavaScript avec un score de 9,1 sur Douban.

 

 

Je suppose que tu aimes

Origine blog.csdn.net/huihui_999/article/details/130994160
conseillé
Classement