Как axios инкапсулирует HTTP-запросы

JavaScript

Статьи, изначально опубликованные на сайте TutorialDocsКак реализовать библиотеку HTTP-запросов с помощью Axios.

Обзор

Во фронтенд-разработке часто встречается сцена отправки асинхронных запросов. Полнофункциональная библиотека HTTP-запросов может значительно сократить наши затраты на разработку и повысить ее эффективность.

axios — это такая библиотека HTTP-запросов, которая стала очень популярной в последние годы. В настоящее время он имеет более 40 000 звезд на GitHub и рекомендуется многими экспертами.

Поэтому нам необходимо понять, как устроен axios и как реализовать инкапсуляцию библиотеки HTTP-запросов. На момент написания текущая версия axios — 0.18.0, и мы используем эту версию в качестве примера для чтения и анализа части основного исходного кода. Все исходные файлы для axios находятся вlibпапка, указанные ниже пути относятся кlibГоворя о.

В этой статье мы в основном обсуждаем:

  • Как использовать аксиомы.

  • Как разработаны и реализованы основные модули 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'))
});

Это официальный пример. Как видно из приведенного выше кода, использование Axios связано с jQuery.ajaxМетоды очень похожи, оба возврата объекта обещания (возможности успеха также могут быть использованы здесь, но все еще более рекомендуемыеPromiseилиawait), прежде чем продолжить.

Этот пример настолько прост, что мне не нужно его объяснять. Давайте посмотрим, как добавить функцию-перехватчик.

Добавить функцию перехватчика

// 添加一个请求拦截器。注意,这里面有 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('请求撤销了', thrown.message);
  } else {
    // 处理错误
  }
});

// 例子二
axios.post('/user/12345', {
  name: '新名字'
}, {
  cancelToken: source.token
}).

// 撤销请求 (信息参数是可选的)
source.cancel('用户撤销了请求');

Как вы можете видеть из приведенного выше примера, в axios, используяCancelTokenсхема запроса на вывод средств. Однако сейчас это предложение было отозвано, как указано вкликните сюда. Конкретный метод реализации запроса на отзыв будет объяснен позже при анализе исходного кода.

Дизайн и реализация основного модуля axios

Благодаря приведенным выше примерам я считаю, что у всех есть общее представление об использовании аксиом. Ниже мы проанализируем дизайн и реализацию axios с точки зрения модулей. На картинке ниже показан файл исходного кода, который я представлю в этой статье. Если вы заинтересованы, лучше всего клонировать соответствующий код во время чтения, это углубит ваше понимание соответствующего модуля.

Модуль HTTP-запроса

Код модуля запроса находится вcore/dispatchRequest.jsВ файле здесь я показываю только некоторые коды клавиш, чтобы кратко объяснить:

module.exports = function dispatchRequest(config) {
    throwIfCancellationRequested(config);

    // 其他源码

    // 默认适配器是一个模块,可以根据当前环境选择使用 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, получить модуль отправки запроса. Мы также можем заменить исходный модуль, передав соответствующую спецификации функцию адаптера (обычно мы этого не делаем, но это слабосвязанная точка расширения).

существуетdefaults.jsВ файле мы видим логику выбора соответствующего адаптера — какой адаптер использовать на основе некоторых уникальных свойств и конструкторов текущего контейнера.

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;
}

Модуль XHR в axios относительно прост, онXMLHTTPRequestИнкапсуляция объекта, я не буду объяснять это здесь. Заинтересованные студенты, вы можете сами прочитать исходный код, исходный код находится по адресуadapters/xhr.jsв файле.

модуль-перехватчик

Теперь давайте посмотрим, как axios обрабатывает функции перехватчика запросов и ответов. Это связано с унифицированным интерфейсом в axios -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;
};

Эта функция является интерфейсом для отправки запросов axios. Поскольку код реализации функции довольно длинный, здесь я кратко расскажу о соответствующих дизайнерских идеях:

  1. chainявляется очередью выполнения. Начальное значение очереди — это конфигурация переноса (config) параметр объекта Promise.

  2. В очереди выполнения исходная функцияdispatchRequestиспользуется для отправки запросов, для связи сdispatchRequestСоответственно добавляемundefined. Добавить кundefinedПричина в том, что вам нужно предоставить функции обратного вызова для успеха и неудачи Promise из следующего кодаpromise = promise.then(chain.shift(), chain.shift());Мы видим, что. Следовательно, функцияdispatchRequestа такжеundefiendЕго можно рассматривать как пару функций.

  3. в очереди на выполнениеchain, который отправил запросdispatchReqeustФункция находится посередине. Ему предшествует перехватчик запросов, использующийunshiftвставка метода; за ним следует перехватчик ответа, использующийpushспособ вставки, вdispatchRequestПозже. Следует отметить, что все эти функции являются парными, то есть одновременно будут вставлены две.

просматривать выше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;

использоватьCancelTokenclass, вам нужно передать ему метод 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) {
            // 已经被撤销了
            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
    };
};

adapters/xhr.jsВ файле место для отзыва запроса прописано так:

if (config.cancelToken) {
    // 等待撤销
    config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
            return;
        }

        request.abort();
        reject(cancel);
        // 重置请求
        request = null;
    });
}

В приведенном выше примере отзыва HTTP-запроса давайте кратко обсудим соответствующую логику реализации:

  1. В запросе, который необходимо отозвать, звонитеCancelTokenКатегорияsourceКласс метода инициализируется и получит содержащийCancelTokenэкземпляр класса A иcancelобъект метода.

  2. Пока исходный метод возвращает экземпляр A, состояние ожиданияpromiseИнициализация объекта завершена. После передачи экземпляра A в axios,promiseЕго можно использовать как триггер для отмены запроса.

  3. при вызове черезsourceметод возвращенcancelметод, в примере ApromiseСтатус меняется с "ожидание" на "выполнено", а затем срабатывает немедленно.thenПерезвоните. Итак, метод отмены axios——request.abort()был запущен.

Каковы преимущества этой конструкции axios?

Логика обработки функции отправки запроса

Как упоминалось в предыдущих главах, axios не будут использоваться для отправки запросов.dispatchRequestФункция рассматривается как специальная функция. Фактически,dispatchRequestОн будет помещен в середину очереди, чтобы обеспечить согласованность обработки очереди и удобочитаемость кода.

Логика обработки адаптера

В логике обработки адаптераhttpа такжеxhrМодуль (один используется в node.js для отправки запросов, один не в браузере для отправки запроса) неdispatchRequestфункция, но каждый как отдельный модуль, по умолчаниюdefaults.jsВводится методом конфигурации в файле. Таким образом, он не только обеспечивает низкую связь между двумя модулями, но и предоставляет будущим пользователям пространство для настройки модуля отправки запросов.

Логика отзыва HTTP-запросов

В логике отмены HTTP-запросов axios предназначен для использования промисов в качестве триггеров, которыеresolveФункция выставляется и используется в функции обратного вызова. Это не только обеспечивает согласованность внутренней логики, но также гарантирует, что при отзыве запроса выборочные данные соответствующего класса не нужно изменять напрямую, чтобы в значительной степени избежать взлома других модулей.

Суммировать

В этой статье подробно рассказывается об использовании, дизайнерских идеях и методах реализации аксиом. После прочтения вы сможете понять структуру axios, а также понять инкапсуляцию и взаимодействие модулей.

В этой статье представлены только основные модули axios.Если вас интересуют коды других модулей, вы можете перейти наGitHubПосмотреть выше.

(над)