Heim > Artikel > Web-Frontend > Axios-Quellcode-Analyse zur Implementierung einer HTTP-Anforderungsbibliothek
Der Inhalt, der in diesem Artikel mit Ihnen geteilt wird, befasst sich mit der Implementierung einer HTTP-Anforderungsbibliothek durch Axios-Quellcode-Analyse. Als nächstes werfen wir einen Blick auf den spezifischen Inhalt.
Während des Front-End-Entwicklungsprozesses stoßen wir häufig auf Situationen, in denen wir asynchrone Anfragen senden müssen. Durch die Verwendung einer HTTP-Anforderungsbibliothek mit vollständigen Funktionen und vollständigen Schnittstellen können wir unsere Entwicklungskosten erheblich senken und unsere Entwicklungseffizienz verbessern.
axios ist eine HTTP-Anforderungsbibliothek, die in den letzten Jahren sehr beliebt geworden ist. Sie hat derzeit mehr als 40.000 Sterne in GitHub und wurde von allen empfohlen.
Heute werfen wir einen Blick darauf, wie Axios aufgebaut ist und was es wert ist, daraus zu lernen. Als ich diesen Artikel schrieb, war die Axios-Version 0.18.0. Nehmen wir diese Version des Codes als Beispiel, um spezifisches Lesen und Analysieren des Quellcodes durchzuführen. Derzeit befinden sich alle Axios-Quellcodedateien im Ordner lib
, daher beziehen sich die unten genannten Pfade auf die Pfade im Ordner lib
.
Der Hauptinhalt dieses Artikels ist:
Wie man Axios verwendet
Wie das Kernmodul von Axios aufgebaut ist und implementiert (Anfragen, Abfangjäger, Abhebungen)
Was es wert ist, aus dem Design von Axios zu lernen
Um das Design von Axios zu verstehen, müssen wir uns zunächst ansehen, wie Axios verwendet wird. Wir stellen die folgende Axios-API anhand eines einfachen Beispiels vor.
axios({ method:'get', url:'http://bit.ly/2mTM3nY', responseType:'stream' }) .then(function(response) { response.data.pipe(fs.createWriteStream('ada_lovelace.jpg')) });
Dies ist ein offizielles API-Beispiel. Aus dem obigen Code können wir erkennen, dass die Verwendung von Axios der von Ajax in jQuery sehr ähnlich ist. Beide setzen nachfolgende Vorgänge fort, indem sie ein Promise zurückgeben (Sie können auch den Success-Callback verwenden, es wird jedoch empfohlen, Promise oder Wait zu verwenden).
Dieses Codebeispiel ist sehr einfach, daher werde ich nicht auf Details eingehen. Schauen wir uns an, wie man eine Filterfunktion hinzufügt.
// 增加一个请求拦截器,注意是2个函数,一个处理成功,一个处理失败,后面会说明这种情况的原因 axios.interceptors.request.use(function (config) { // 请求发送前处理 return config; }, function (error) { // 请求错误后处理 return Promise.reject(error); }); // 增加一个响应拦截器 axios.interceptors.response.use(function (response) { // 针对响应数据进行处理 return response; }, function (error) { // 响应错误后处理 return Promise.reject(error); });
Anhand des obigen Beispiels können wir Folgendes erkennen: Bevor die Anfrage gesendet wird, können wir eine Datenverarbeitung für den Konfigurationsparameter der Anfrage durchführen; Nachdem wir eine Antwort angefordert haben, können wir auch bestimmte Vorgänge an den zurückgegebenen Daten durchführen. Gleichzeitig können wir eine spezifische Fehlerbehandlung durchführen, wenn die Anfrage fehlschlägt und die Antwort fehlschlägt.
Beim Ausführen suchbezogener Funktionen müssen wir häufig häufig Anfragen zur Datenabfrage senden. Im Allgemeinen müssen wir beim Senden der nächsten Anfrage die vorherige Anfrage stornieren. Daher ist auch die Möglichkeit, Anfragen zu stornieren, ein Plus. Der Beispielcode für die Axios-Stornierungsanfrage lautet wie folgt:
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function(thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // cancel the request (the message parameter is optional) source.cancel('Operation canceled by the user.');
Aus dem obigen Beispiel können wir sehen, dass Axios einen auf CancelToken basierenden Auszahlungsvorschlag verwendet. Der Vorschlag wurde jedoch inzwischen zurückgezogen. Einzelheiten finden Sie hier. In den folgenden Kapiteln werden wir die spezifische Methode zur Auszahlungsimplementierung in der Quellcode-Analyse erläutern.
Durch die obigen Beispiele glaube ich, dass jeder ein allgemeines Verständnis für die Verwendung von Axios hat. Als nächstes analysieren wir das Design und die Implementierung von Axios nach Modulen. Das Bild unten zeigt die relevanten Axios-Dateien, die wir in diesem Blog behandeln. Wenn Leser interessiert sind, können sie sie durch den klonbezogenen Code und den Blog lesen, was ihr Verständnis der relevanten Module vertiefen kann.
Als Kernmodul befindet sich der Code für das Senden von Anfragen durch Axios in der Datei core/dispatchReqeust.js
. Aus Platzgründen werde ich unten einige wichtige Quellcodes für eine kurze Einführung auswählen:
module.exports = function dispatchRequest(config) { throwIfCancellationRequested(config); // 其他源码 // default adapter是一个可以判断当前环境来选择使用Node还是XHR进行请求发送的模块 var adapter = config.adapter || defaults.adapter; return adapter(config).then(function onAdapterResolution(response) { throwIfCancellationRequested(config); // 其他源码 return response; }, function onAdapterRejection(reason) { if (!isCancel(reason)) { throwIfCancellationRequested(config); // 其他源码 return Promise.reject(reason); }); };
Aus dem obigen Code und den Beispielen können wir erkennen, dass die dispatchRequest
-Methode das Modul erhält, das die Anfrage sendet, indem sie es abruft config.adapter
, wir können das native Modul auch ersetzen, indem wir die Adapterfunktion übergeben, die der Spezifikation entspricht (obwohl dies im Allgemeinen nicht geschieht, kann es als lose gekoppelter Erweiterungspunkt betrachtet werden).
In der default.js
-Datei können wir die relevante Adapterauswahllogik sehen, die auf einigen Eigenschaften und Konstruktoren basiert, die für den aktuellen Container einzigartig sind.
function getDefaultAdapter() { var adapter; // 只有Node.js才有变量类型为process的类 if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { // Node.js请求模块 adapter = require('./adapters/http'); } else if (typeof XMLHttpRequest !== 'undefined') { // 浏览器请求模块 adapter = require('./adapters/xhr'); } return adapter; }
Das XHR-Modul in axios ist eine Kapselung des XMLHTTPRequest-Objekts. Wir werden es hier nicht im Detail vorstellen. Der Code befindet sich im adapters/xhr.js
Datei.
Nachdem wir das von dispatchRequest
implementierte Modul zum Senden von HTTP-Anfragen verstanden haben, werfen wir einen Blick darauf, wie Axios die Funktionen zum Abfangen von Anfragen und Antworten handhabt. Werfen wir einen Blick auf die in Axios angeforderte einheitliche Eintragsfunktion request
.
Axios.prototype.request = function request(config) { // 其他代码 var chain = [dispatchRequest, undefined]; var promise = Promise.resolve(config); this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { chain.push(interceptor.fulfilled, interceptor.rejected); }); while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise; };
Diese Funktion ist der Einstiegspunkt für Axios zum Senden von Anfragen. Da die Funktionsimplementierung relativ lang ist, werde ich kurz auf die relevanten Designideen eingehen:
chain是一个执行队列。这个队列的初始值,是一个带有config参数的Promise。
在chain执行队列中,插入了初始的发送请求的函数dispatchReqeust
和与之对应的undefined
。后面需要增加一个undefined
是因为在Promise中,需要一个success和一个fail的回调函数,这个从代码promise = promise.then(chain.shift(), chain.shift());
就能够看出来。因此,dispatchReqeust
和undefined
我们可以成为一对函数。
在chain执行队列中,发送请求的函数dispatchReqeust
是处于中间的位置。它的前面是请求拦截器,通过unshift
方法放入;它的后面是响应拦截器,通过push
放入。要注意的是,这些函数都是成对的放入,也就是一次放入两个。
通过上面的request
代码,我们大致知道了拦截器的使用方法。接下来,我们来看下如何取消一个HTTP请求。
取消请求相关的模块在Cancel/
文件夹中。让我们来看下相关的重点代码。
首先,让我们来看下元数据Cancel
类。它是用来记录取消状态一个类,具体代码如下:
function Cancel(message) { this.message = message; } Cancel.prototype.toString = function toString() { return 'Cancel' + (this.message ? ': ' + this.message : ''); }; Cancel.prototype.__CANCEL__ = true;
而在CancelToken类中,它通过传递一个Promise的方法来实现了HTTP请求取消,然我们看下具体的代码:
function CancelToken(executor) { if (typeof executor !== 'function') { throw new TypeError('executor must be a function.'); } var resolvePromise; this.promise = new Promise(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); }); } CancelToken.source = function source() { var cancel; var token = new CancelToken(function executor(c) { cancel = c; }); return { token: token, cancel: cancel }; };
而在adapter/xhr.js
文件中,有与之相对应的取消请求的代码:
if (config.cancelToken) { // 等待取消 config.cancelToken.promise.then(function onCanceled(cancel) { if (!request) { return; } request.abort(); reject(cancel); // 重置请求 request = null; }); }
结合上面的取消HTTP请求的示例和这些代码,我们来简单说下相关的实现逻辑:
在可能需要取消的请求中,我们初始化时调用了source方法,这个方法返回了一个CancelToken
类的实例A和一个函数cancel。
在source方法返回实例A中,初始化了一个在pending状态的promise。我们将整个实例A传递给axios后,这个promise被用于做取消请求的触发器。
当source方法返回的cancel方法被调用时,实例A中的promise状态由pending变成了fulfilled,立刻触发了then的回调函数,从而触发了axios的取消逻辑——request.abort()
。
在之前的章节中有提到过,axios在处理发送请求的dispatchRequest
函数时,没有当做一个特殊的函数来对待,而是采用一视同仁的方法,将其放在队列的中间位置,从而保证了队列处理的一致性,提高了代码的可阅读性。
在adapter的处理逻辑中,axios没有把http和xhr两个模块(一个用于Node.js发送请求,另一个则用于浏览器端发送请求)当成自身的模块直接在dispatchRequest
中直接饮用,而是通过配置的方法在default.js
文件中进行默认引入。这样既保证了两个模块间的低耦合性,同时又能够为今后用户需要自定义请求发送模块保留了余地。
在取消HTTP请求的逻辑中,axios巧妙的使用了一个Promise来作为触发器,将resolve函数通过callback中参数的形式传递到了外部。这样既能够保证内部逻辑的连贯性,也能够保证在需要进行取消请求时,不需要直接进行相关类的示例数据改动,最大程度上避免了侵入其他的模块。
本文对axios相关的使用方式、设计思路和实现方法进行了详细的介绍。读者能够通过上述文章,了解axios的设计思想,同时能够在axios的代码中,学习到关于模块封装和交互等相关的经验。
由于篇幅原因,本文仅针对axios的核心模块进行了分解和介绍,如果对其他代码有兴趣的同学,可以去GitHub进行查看。
相关推荐:
Das obige ist der detaillierte Inhalt vonAxios-Quellcode-Analyse zur Implementierung einer HTTP-Anforderungsbibliothek. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!