пакетные аксиомы

axios
пакетные аксиомы

предисловие

axiosлегкийHTTP客户端, который основан наXMLHttpRequestСервис для выполнения HTTP-запросов, поддерживает богатую конфигурацию, поддерживаетPromise, который поддерживает браузер иNode.jsконец. Начиная с Vue2.0, Ю Да (автор Vue Ю Юйси) объявил об отменеvue-resourceофициальная рекомендация, которая, в свою очередь, рекомендуетaxios. СейчасaxiosОн стал первым выбором для большинства разработчиков Vue. (Если вы не знакомы сaxios, допустимыйздесьПроверьте его API).

axiosAPI очень удобный, и вы можете легко использовать его прямо в своем проекте. Однако по мере увеличения масштаба проекта каждый раз, когда инициируется HTTP-запрос, должны выполняться такие операции, как установка периода ожидания, установка заголовка запроса, определение того, какой адрес запроса использовать в соответствии со средой проекта, обработка ошибок и т. д. быть написанным на месте Сумасшедший! Такое дублирование усилий не только тратит время, но и делает код избыточным и трудным для сопровождения.

Чтобы улучшить качество нашего кода, мы должны дважды инкапсулировать его в проекте.axiosповторное использование.

Итак, как инкапсулироватьaxiosШерстяная ткань?

оригинальный внешний вид

Перед инкапсуляцией давайте посмотрим, как выглядит запрос axios в реальном проекте без инкапсуляции. Это выглядит так:

axios('http://localhost:3000/data', {
  method: 'GET',
  timeout: 1000,
  withCredentials: true,
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'xxx',
  },
  transformRequest: [function (data, headers) {
    return data;
  }],
  // 其他请求配置...
})
.then((data) => {
  // todo: 真正业务逻辑代码
  console.log(data);
}, (err) => {
  if (err.response.status === 401) {
  // handle authorization error
  }
  if (err.response.status === 403) {
  // handle server forbidden error
  }
  // 其他错误处理.....
  console.log(err);
});

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

Шаг упаковки

Суть инкапсуляции заключается в том, чтобы добавлять различные вещи за пределы инкапсулируемого контента, а затем представлять их пользователю как новое целое для достижения цели расширения и простоты использования.

упаковкаaxiosНужно заранее настроить конфигурацию, разделяемую всеми HTTP-запросами на axios, зарезервировать нужные параметры и интерфейсы, а затем вернуть как новый axios.

Затем мы используем демонстрацию для реализации хорошо расширяемойaxiosупаковка.

Структура демонстрационного каталога выглядит следующим образом (сгенерирована Vue-cli 3.0):

|--public/
|--mock/
|   |--db.json  # 我新建的接口模拟数据
|--src/
|   |--assets/
|   |--components/
|   |--router/
|   |--store/
|   |--views/
|       |--Home.Vue
|   |--App.vue
|   |--main.js
|   |--theme.styl
|--package.json
|...

цель пакета

Я хочу сделать запрос axios на домашней странице таким же простым, как вызов метода с несколькими параметрами, чтобы я мог сосредоточиться на бизнес-коде.

1. Упакуйте аксиомы в отдельный файл

  • Создание файла Utils / http.js под SRC
cd src
mkdir utils
touch http.js
  • Представлены аксиомы
// src/utils/http.js

import axios from 'axios';
  • создать класс Вы также можете использовать функции для инкапсуляции, я просто думаю, что классы более семантичны.
//src/utils/http.js

//...
class NewAxios {

}
  • Настройте разные адреса запросов для разных сред согласно сprocess.env.NODE_ENVНастроить отличатьсяbaseURL, чтобы проект мог автоматически переключать адрес узла запроса в разных средах, просто выполняя соответствующую команду упаковки.
// src/utils/http.js

//...
const getBaseUrl = (env) => {
  let base = {
    production: '/',
    development: 'http://localhost:3000',
    test: 'http://localhost:3001',
  }[env];
  if (!base) {
    base = '/';
  }
  return base;
};

class NewAxios {
  constructor() {
    this.baseURL = getBaseUrl(process.env.NODE_ENV);
  }
}
  • Настройка тайм-аута Свойство Timeout, я обычно устанавливаю на 10 секунд.
// src/utils/http.js

//...
class NewAxios {
  constructor() {
    //...
    this.timeout = 10000;
  }
}
  • Настройте, чтобы разрешить учетные данные Для свойства widthCredentials установлено значение true.
// src/utils/http.js

//...
class NewAxios {
  constructor() {
    //...
    this.withCredentials = true;
  }
}
  • Создайте запрос метода для экземпляра этого класса существуетrequestВ методе создается новый экземпляр axios, принимаются параметры конфигурации запроса, обрабатываются параметры, добавляется конфигурация и возвращается результат запроса (объект обещания) экземпляра axios. Вы также можете использовать экспортированный экземпляр axios по умолчанию, не создавая его, и поместить в него всю конфигурацию, но таким образом весь проект будет совместно использовать один экземпляр axios. Хотя для большинства проектов этого достаточно, структуры запросов и ответов разных сервисных адресов в некоторых проектах могут быть совершенно разными, в этом случае совместное использование экземпляра не поддерживается. Итак, чтобы инкапсулировать более общий и более гибкий, я буду использовать метод создания axios, чтобы каждый запрос был новым экземпляром axios.
// src/utils/http.js

//...
class NewAxios {
  //...
  request(options) {
    // 每次请求都会创建新的axios实例。
    const instance = axios.create();
    const config = { // 将用户传过来的参数与公共配置合并。
      ...options,
      baseURL: this.baseURL,
      timeout: this.timeout,
      withCredentials: this.withCredentials,
    };
    // 配置拦截器,支持根据不同url配置不同的拦截器。
    this.setInterceptors(instance, options.url);
    return instance(config); // 返回axios实例的执行结果
  }
}

Поскольку содержимое конфигурации перехватчика больше, оно инкапсулировано во внутреннюю функцию.

  • Настроить перехватчик запросов Здесь единообразно настраиваются все изменения, внесенные в параметры запроса перед отправкой запроса. Например, единообразно добавлять учетные данные токена, единообразно задавать язык, единообразно устанавливать тип контента, указывать формат данных и т. д. Не забудьте вернуть эту конфигурацию, когда закончите, иначе весь запрос не будет обработан. Я настрою токен здесь.
// src/utils/http.js

//...
class NewAxios {
  //...
  // 这里的url可供你针对需要特殊处理的接口路径设置不同拦截器。
  setInterceptors = (instance, url) => { 
    instance.interceptors.request.use((config) => { // 请求拦截器
      // 配置token
      config.headers.AuthorizationToken = localStorage.getItem('AuthorizationToken') || '';
      return config;
    }, err => Promise.reject(err));
  }
  //...
}
  • Настроить перехватчик ответа по требованиюthenилиcatchПеред обработкой данных ответа выполняется цикл предварительной обработки. Например, фильтрация данных ответа и многое другое здесь предназначено для выполнения унифицированной обработки ошибок для различных кодов ошибок ответа, а также обработки отключения сети и т. д. Я буду судить 403 и разъединение здесь.
// src/utils/http.js

//...
class NewAxios {
  //...
  setInterceptors = (instance, url) => {
    //...
    instance.interceptors.response.use((response) => { // 响应拦截器
      // todo: 想根据业务需要,对响应结果预先处理的,都放在这里
      console.log();
      return response;
    }, (err) => {
      if (err.response) { // 响应错误码处理
        switch (err.response.status) {
          case '403':
            // todo: handler server forbidden error
            break;
            // todo: handler other status code
          default:
            break;
        }
        return Promise.reject(err.response);
      }
      if (!window.navigator.online) { // 断网处理
        // todo: jump to offline page
        return -1;
      }
      return Promise.reject(err);
    });
  }
  //...
}

Кроме того, в перехватчике также удобно размещать такие эффекты буферизации, как загрузка: отображать загрузку в перехватчике запросов, а убирать загрузку в перехватчике ответов. Таким образом, все запросы имеют единый эффект загрузки.

  • Экспорт новых экземпляров по умолчанию
// src/utils/http.js

//...
export default new NewAxios();

Окончательный полный код выглядит следующим образом:

// src/utils/http.js

import axios from 'axios';

const getBaseUrl = (env) => {
  let base = {
    production: '/',
    development: 'http://localhost:3000',
    test: 'http://localhost:3001',
  }[env];
  if (!base) {
    base = '/';
  }
  return base;
};

class NewAxios {
  constructor() {
    this.baseURL = getBaseUrl(process.env.NODE_ENV);
    this.timeout = 10000;
    this.withCredentials = true;
  }

  setInterceptors = (instance, url) => {
    instance.interceptors.request.use((config) => {
      // 在这里添加loading
      // 配置token
      config.headers.AuthorizationToken = localStorage.getItem('AuthorizationToken') || '';
      return config;
    }, err => Promise.reject(err));

    instance.interceptors.response.use((response) => {
      // 在这里移除loading
      // todo: 想根据业务需要,对响应结果预先处理的,都放在这里
      return response;
    }, (err) => {
      if (err.response) { // 响应错误码处理
        switch (err.response.status) {
          case '403':
            // todo: handler server forbidden error
            break;
            // todo: handler other status code
          default:
            break;
        }
        return Promise.reject(err.response);
      }
      if (!window.navigator.online) { // 断网处理
        // todo: jump to offline page
        return -1;
      }
      return Promise.reject(err);
    });
  }

  request(options) {
    // 每次请求都会创建新的axios实例。
    const instance = axios.create();
    const config = { // 将用户传过来的参数与公共配置合并。
      ...options,
      baseURL: this.baseURL,
      timeout: this.timeout,
      withCredentials: this.withCredentials,
    };
    // 配置拦截器,支持根据不同url配置不同的拦截器。
    this.setInterceptors(instance, options.url);
    return instance(config); // 返回axios实例的执行结果
  }
}

export default new NewAxios();

СейчасaxiosУпаковка готова на 80%. Нам все еще нужно объединить axios с интерфейсом и инкапсулировать еще один уровень, чтобы достичь цели инкапсуляции, которую я поставил в начале.

2. Используйте новый API-интерфейс axios.

  • Создайте новый в каталоге srcapiпапка. Все интерфейсы, связанные с HTTP-запросами, унифицированы и управляются в этом каталоге.
  • новыйhome.js. Нам нужно классифицировать интерфейсы по определенным правилам, а класс интерфейсов соответствует js-файлу. Эту категорию можно разделить по страницам, по модулям и так далее. Чтобы продемонстрировать более интуитивно, я разделю его здесь по страницам. На самом деле это зависит от ваших потребностей.
  • Используйте новые аксиомы для инкапсуляции API (исправьте значение URL-адреса, объедините параметры, переданные пользователем), а затем назовите и экспортируйте эти функции.
// src/api/home.js 

import axios from '@/utils/http';
export const fetchData = options => axios.request({
  ...options,
  url: '/data',
});
export default {};
  • Создайте новый в каталоге APIindex.js, и экспортировать в этот файл интерфейсы других файлов.
// src/api/index.js

export * from './home';

Этот уровень инкапсуляции инкапсулирует наши новые аксиомы в более лаконичный и семантический метод интерфейса.

Теперь наша структура каталогов выглядит так:

|--public/
|--mock/
|   |--db.json  # 接口模拟数据
|--src/
|   |--api/     # 所有的接口都集中在这个目录下
|       |--home.js  # Home页面里涉及到的接口封装在这里
|       |--index.js # 项目中所有接口调用的入口
|   |--assets/
|   |--components/
|   |--router/
|   |--store/
|   |--utils/
|       |--http.js  # axios封装在这里
|   |--views/
|       |--Home.Vue
|   |--App.vue
|   |--main.js
|   |--theme.styl
|--package.json
|...

Используйте упакованные axios

Теперь, когда мы хотим отправлять HTTP-запросы, нам просто нужно ввестиapiвнизindex.jsФайлы могут вызывать любой интерфейс и использовать инкапсулированныйaxios.

// src/views/Home.vue

<template>
  <div class="home">
    <h1>This is home page</h1>
  </div>
</template>

<script>
// @ is an alias to /src
import { fetchData } from '@/api/index';

export default {
  name: 'home',
  mounted() {
    fetchData()  // axios请求在这里
      .then((data) => {
        console.log(data);
      })
      .catch((err) => {
        console.log(err);
      });
  },
};
</script>

запросы axios инкапсулируются вfetchDataВ функции запрос страницы не должен появлятьсяaxios API, молча отправляя запрос и получая ответ, точно так же, как вызов простогоPromiseфункционировать так же легко. А на странице вам нужно сосредоточиться только на обработке бизнес-функций, не отвлекаясь на другие вещи.

бегать

бегатьnpm run serveзапустить проект, выполнитьnpm run mockЗапустите фиктивный интерфейс службы.

Открой сейчасlocalhost:8080Вы можете увидеть домашнюю страницу. Откройте консоль браузера, и вы увидите напечатанный результат ответа на запрос:

wrap-axios.jpg

Простой и элегантный.

Суммировать

  1. Идея инкапсуляции является очень полезной идеей в интерфейсных технологиях. Простые аксио и интерфейсная инкапсуляция позволяют нам оценить его очарование.
  2. упаковкаaxiosНе существует абсолютного стандарта, если ваш пакет может удовлетворить потребности вашего проекта и удобен в использовании, это хорошее пакетное решение.
  3. BTW: Вышеупомянутый пакет предоставляет вам упакованную структуру axios и API, которая упакована с помощью описанного выше процесса.axios, не ограничиваясь Vue, также можно использовать проекты React, он подходит для любого внешнего проекта.

Код для этой статьи можно найти здесь:Таким образом, GitHub.com/111/wrap-...

Добро пожаловать на общение~

Добро пожаловать в перепечатку, пожалуйста, укажите источник:Найдите Pinyin.com/2019/12/23/…