>  기사  >  웹 프론트엔드  >  Axios 소스 코드 분석, HTTP 요청 라이브러리 구현 방법

Axios 소스 코드 분석, HTTP 요청 라이브러리 구현 방법

不言
不言원래의
2018-07-25 10:15:511524검색

이 글에서 공유한 내용은 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 또는 wait를 사용하는 것이 좋습니다). 🎜🎜이 코드 예제는 매우 간단하므로 자세히 설명하지 않겠습니다. 필터 기능을 추가하는 방법을 살펴보겠습니다. 🎜

인터셉터 기능 추가🎜

    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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.