Axios ソースコード分析 HTTP リクエスト ライブラリの実装方法

不言
リリース: 2018-07-25 10:15:51
オリジナル
1502 人が閲覧しました

この記事で共有する内容は、axios のソースコード分析を通じて HTTP リクエスト ライブラリを実装する方法に関するものです。次に、具体的な内容を見てみましょう。

概要

フロントエンド開発プロセスでは、非同期リクエストを送信する必要がある状況によく遭遇します。完全な機能と完全なインターフェイスを備えた HTTP リクエスト ライブラリを使用すると、開発コストを大幅に削減し、開発効率を向上させることができます。

axios は、近年非常に人気のある HTTP リクエスト ライブラリであり、現在 GitHub に 40,000 個以上のスターがあり、誰もが推奨しています。

今日は、axios がどのように設計されているか、そしてそこから学ぶ価値があることを見てみましょう。この記事を書いた時点ではaxiosのバージョンは0.18.0でした。このバージョンのコードを例として、特定のソース コードの読み取りと分析を行ってみましょう。現在、すべての axios ソース コード ファイルは lib フォルダー内にあるため、以下で説明するパスは lib フォルダー内のパスを指します。 lib文件夹中,因此我们下文中提到的路径均是指lib文件夹中的路径。

本文的主要内容有:

  • 如何使用axios

  • axios的核心模块是如何设计与实现的(请求、拦截器、撤回)

  • axios的设计有什么值得借鉴的地方

如何使用axios

想要了解axios的设计,我们首先需要来看下axios是如何使用的。我们通过一个简单示例来介绍以下axios的API。

发送请求

axios({
  method:'get',
  url:'http://bit.ly/2mTM3nY',
  responseType:'stream'
})
  .then(function(response) {
  response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
ログイン後にコピー

这是一个官方的API示例。从上面的代码中我们可以看到,axios的用法与jQuery的ajax很相似,都是通过返回一个Promise(也可以通过success的callback,不过建议使用Promise或者await)来继续后面的操作。

这个代码示例很简单,我就不过多赘述了,下面让我们来看下如何添加一个过滤器函数。

增加拦截器(Interceptors)函数

// 增加一个请求拦截器,注意是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);
  });
ログイン後にコピー

通过上面的示例我们可以知道:在请求发送前,我们可以针对请求的config参数进行数据处理;而在请求响应后,我们也能针对返回的数据进行特定的操作。同时,在请求失败和响应失败时,我们都可以进行特定的错误处理。

取消HTTP请求

在完成搜索相关的功能时,我们经常会需要频繁的发送请求来进行数据查询的情况。通常来说,我们在下一次请求发送时,就需要取消上一次请求。因此,取消请求相关的功能也是一个优点。axios取消请求的示例代码如下:

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.');
ログイン後にコピー

通过上面的示例我们可以看到,axios使用的是基于CancelToken的一个撤回提案。不过,目前该提案已经被撤回,具体详情可以见此处。具体的撤回实现方法我们会在后面的章节源码分析的时候进行说明。

axios的核心模块是如何设计与实现的

通过上面的例子,我相信大家对axios的使用方法都有了一个大致的了解。下面,我们将按照模块来对axios的设计与实现进行分析。下图是我们在这篇博客中将会涉及到的相关的axios的文件,如果读者有兴趣的话,可以通过clone相关代码结合博客进行阅读,这样能够加深对相关模块的理解。

HTTP请求模块

作为核心模块,axios发送请求相关的代码位于core/dispatchReqeust.js文件中。由于篇幅有限,下面我选取部分重点的源码进行简单的介绍:

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);
        });
};
ログイン後にコピー

通过上面的代码和示例我们可以知道,dispatchRequest方法是通过获取config.adapter来得到发送请求的模块的,我们自己也可以通过传入符合规范的adapter函数来替换掉原生的模块(虽然一般不会这么做,不过也算是一个松耦合扩展点)。

default.js文件中,我们能够看到相关的adapter选择逻辑,即根据当前容器中特有的一些属性和构造函数来进行判断。

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;
}
ログイン後にコピー

axios中XHR模块较为简单,为XMLHTTPRequest对象的封装,我们在这里就不过多进行介绍了,有兴趣的同学可以自行阅读,代码位于adapters/xhr.js文件中。

拦截器模块

了解了dispatchRequest实现的HTTP请求发送模块,我们来看下axios是如何处理请求和响应拦截函数的。让我们看下axios中请求的统一入口request

この記事の主な内容は次のとおりです:

  • axios の使用方法🎜
  • 🎜 axios のコア モジュールがどのように設計され実装されているか(リクエスト、インターセプター、撤退)🎜
  • 🎜axios の設計から学ぶべきこと🎜
🎜🎜axios の使い方🎜🎜🎜 axios の設計を理解するには、まず、axios がどのように使用されるかを見てみましょう。以下の axios API を簡単な例を通して紹介します。 🎜

リクエストを送信🎜

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;
};
ログイン後にコピー
🎜これは公式 API の例です。上記のコードから、axios の使用法は jQuery の ajax と非常に似ていることがわかります。どちらも Promise を返すことで後続の操作を続行します (成功コールバックを使用することもできますが、Promise または await を使用することをお勧めします)。 🎜🎜このコード例は非常に簡単なので、フィルター関数の追加方法については説明しません。 🎜

インターセプター関数の追加🎜

    function Cancel(message) {
      this.message = message;
    }

    Cancel.prototype.toString = function toString() {
      return 'Cancel' + (this.message ? ': ' + this.message : '');
    };

    Cancel.prototype.__CANCEL__ = true;
ログイン後にコピー
ログイン後にコピー
🎜 上記の例を通じて、リクエストが送信される前に、リクエストを構成できることがわかります。 パラメーターは次の目的で使用されます。データ処理、応答をリクエストした後、返されたデータに対して特定の操作を実行することもできます。同時に、リクエストが失敗し、応答が失敗した場合に、特定のエラー処理を実行できます。 🎜

HTTP リクエストをキャンセルする🎜

🎜 検索関連の機能を完了するとき、データ クエリのリクエストを頻繁に送信する必要があることがよくあります。一般に、次のリクエストを送信するときは、前のリクエストをキャンセルする必要があります。したがって、リクエストのキャンセルに関する機能もプラスになります。 axios キャンセルリクエストのサンプルコードは次のとおりです: 🎜
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
    };
};
ログイン後にコピー
ログイン後にコピー
🎜 上記の例を通じて、axios が CancelToken に基づいた引き出し提案を使用していることがわかります。ただし、この提案は現在撤回されています。詳細はこちらをご覧ください。ソースコード解析における具体的な引き出しの実装方法については、次章以降で解説していきます。 🎜🎜🎜axios のコアモジュールがどのように設計され、実装されているか🎜🎜🎜 上記の例を通して、誰もが axios の使用方法を一般的に理解していると思います。次に、axios の設計と実装をモジュールごとに分析します。下の図は、このブログで取り上げる関連する axios ファイルを示しています。読者が興味がある場合は、クローン関連のコードとブログを通じて読むことができ、関連するモジュールについての理解を深めることができます。 🎜

HTTP リクエスト モジュール🎜

🎜コア モジュールとして、axios のリクエスト送信に関連するコードは core/dispatchReqeust.js にあります。 ファイルの中間。スペースが限られているため、以下でいくつかの主要なソース コードを選択して簡単に紹介します: 🎜
if (config.cancelToken) {
    // 等待取消
    config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
            return;
        }

        request.abort();
        reject(cancel);
        // 重置请求
        request = null;
    });
}
ログイン後にコピー
ログイン後にコピー
🎜 上記のコードと例を通じて、dispatchRequest メソッドが config を取得するものであることがわかります。 .adapterリクエストを送信するモジュールを取得するために、仕様に準拠したアダプター関数を渡すことでネイティブ モジュールを置き換えることもできます (これは一般には行われませんが、疎結合拡張子とみなすことができます)ポイント)。 🎜🎜 default.js ファイルでは、現在のコンテナに固有のいくつかのプロパティとコンストラクターに基づいた、関連するアダプター選択ロジックを確認できます。 🎜rrreee🎜 axios の XHR モジュールは、XMLHTTPRequest オブジェクトのカプセル化です。興味のある学生は、adapters/xhr にあります。 .js ファイル。 🎜

インターセプター モジュール🎜

🎜dispatchRequest によって実装された HTTP リクエスト送信モジュールを理解した上で、axios がどのように機能するかを見てみましょうリクエストとレスポンスのインターセプト機能を処理します。 axios で要求される統合エントリ request 関数を見てみましょう。 🎜rrreee🎜この関数は、axios がリクエストを送信するためのエントリ ポイントです。関数の実装は比較的長いため、関連する設計アイデアについて簡単に説明します。
  1. chain是一个执行队列。这个队列的初始值,是一个带有config参数的Promise。

  2. 在chain执行队列中,插入了初始的发送请求的函数dispatchReqeust和与之对应的undefined。后面需要增加一个undefined是因为在Promise中,需要一个success和一个fail的回调函数,这个从代码promise = promise.then(chain.shift(), chain.shift());就能够看出来。因此,dispatchReqeustundefined我们可以成为一对函数。

  3. 在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请求的示例和这些代码,我们来简单说下相关的实现逻辑:

  1. 在可能需要取消的请求中,我们初始化时调用了source方法,这个方法返回了一个CancelToken类的实例A和一个函数cancel。

  2. 在source方法返回实例A中,初始化了一个在pending状态的promise。我们将整个实例A传递给axios后,这个promise被用于做取消请求的触发器。

  3. 当source方法返回的cancel方法被调用时,实例A中的promise状态由pending变成了fulfilled,立刻触发了then的回调函数,从而触发了axios的取消逻辑——request.abort()

axios的设计有什么值得借鉴的地方

发送请求函数的处理逻辑

在之前的章节中有提到过,axios在处理发送请求的dispatchRequest函数时,没有当做一个特殊的函数来对待,而是采用一视同仁的方法,将其放在队列的中间位置,从而保证了队列处理的一致性,提高了代码的可阅读性。

Adapter的处理逻辑

在adapter的处理逻辑中,axios没有把http和xhr两个模块(一个用于Node.js发送请求,另一个则用于浏览器端发送请求)当成自身的模块直接在dispatchRequest中直接饮用,而是通过配置的方法在default.js文件中进行默认引入。这样既保证了两个模块间的低耦合性,同时又能够为今后用户需要自定义请求发送模块保留了余地。

取消HTTP请求的处理逻辑

在取消HTTP请求的逻辑中,axios巧妙的使用了一个Promise来作为触发器,将resolve函数通过callback中参数的形式传递到了外部。这样既能够保证内部逻辑的连贯性,也能够保证在需要进行取消请求时,不需要直接进行相关类的示例数据改动,最大程度上避免了侵入其他的模块。

总结

本文对axios相关的使用方式、设计思路和实现方法进行了详细的介绍。读者能够通过上述文章,了解axios的设计思想,同时能够在axios的代码中,学习到关于模块封装和交互等相关的经验。

由于篇幅原因,本文仅针对axios的核心模块进行了分解和介绍,如果对其他代码有兴趣的同学,可以去GitHub进行查看。

相关推荐:

React组件中绑定this的原因分析

angularjs关于页面模板清除的使用方法

以上がAxios ソースコード分析 HTTP リクエスト ライブラリの実装方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!