👻👻 идея пакета axios + централизованное управление API

внешний интерфейс JavaScript
👻👻 идея пакета axios + централизованное управление API

«Это третий день моего участия в первом испытании обновлений 2022 года. Подробную информацию о мероприятии см.:Вызов первого обновления 2022 г."

предисловие

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

Прочитав эту статью, вы получите:

  • как использоватьtypescriptинкапсуляция классаaxios
    • Идея инкапсуляции сетевых запросов
    • Определите сетевой запрос вdataтип данных
    • Как настроитьviteпеременные среды

Установить

Выполните следующую команду в корневом каталоге проекта для установкиaxiosа такжеantd-mobile.

использоватьnpmДля установки выполните следующую команду

npm install --save antd-mobile@next axios

использоватьyarnДля установки выполните следующую команду

yarn add antd-mobile@next axios

использоватьpnpmДля установки выполните следующую команду

pnpm add antd-mobile@next axios

Основное использование аксиом

axios.request(config)

axios.get(url[, config])

axios.delete(url[, config])

axios.head(url[, config])

axios.options(url[, config])

axios.post(url[, data[, config]])

axios.put(url[, data[, config]])

axios.patch(url[, data[, config]])

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

цель пакета

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

image-20220120005713912

новое в проектеnetworkФайл, как корневая папка веб-запроса проекта. Мы поставим соответствующие файлы конфигурации здесь в будущем.

mkdir network

создать в этой корневой папкеapi,Interceptors,typesпапки иrequest.tsфайлы, которые используются для храненияapiЦентрализованное управление, перехватчик сетевых запросов, возвращаемые интерфейсом данные, класс сетевых запросов.

├─api
├─Interceptors
├─types
└─request.ts

Написать библиотеку сетевых запросов

существуетrequest.tsсоздать новыйApiкласс и экспортировать его.

export default class Api{
  
}
нужно

Прежде чем писать этот класс, нам нужно спроектировать, что этот класс будет делать для нас.

  • Инициализируйте параметры конфигурации сетевого запроса (например:urlпрефикс, время ожидания сети)
  • Сетевой перехватчик (запрос и ответ)
  • по требованиюloading
Написание интерфейса типа

существуетrequest.tsОпределите тип полученного параметра в нативномAxiosRequestConfigдобавлен интерфейсloadingа такжеinterceptorатрибут используется для отображенияloadingToastи нужно ли настраивать сетевой перехватчик.

import axios, {AxiosInstance, AxiosRequestConfig, AxiosResponse,} from 'axios';
export interface ApiType extends AxiosRequestConfig{
    loading?:boolean
    interceptor?:Interceptor
}

export interface Interceptor {
    requestInterceptor: (res: AxiosRequestConfig) => (AxiosRequestConfig)
    requestInterceptorErr?: (error: any) => any
    responseInterceptor: (res: AxiosResponse) => (AxiosResponse)
    responseInterceptorErr?: (error: any) => any
}
Общая конфигурация пакета

существуетconstructorФункция получает часто используемые элементы конфигурации и инициализирует ихAxiosInstance.

export default class Api {
    instance: AxiosInstance;
    config: AxiosRequestConfig;
    interceptor?: Interceptor;
    loading: boolean;

    constructor(option: ApiType) {
        this.config = option;
        this.interceptor = option.interceptor;
        this.loading=option.loading??true
        // 配置全局参数
        this.instance = axios.create(this.config);
}

?? — оператор объединения null в ES2020, он используется для присвоения значений по умолчанию переменным.

инкапсулировать перехватчик

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

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

constructor(option: ApiType) {
    // ...
    // 拦截器
    // 配置请求拦截器
    this.instance.interceptors.request.use(this.interceptor?.requestInterceptor, this.interceptor?.requestInterceptorErr);
    // 配置响应拦截器
    this.instance.interceptors.response.use(this.interceptor?.responseInterceptor, 		this.interceptor?.responseInterceptorErr);
}

Добавить кloadingэффект, потому что в текущем проекте я ввелantd-mobileФреймворк пользовательского интерфейса, я просто использую его собственныйToastКомпоненты.

Вы также можете использовать некоторыеloadingизcssБиблиотеки, например:CSS Loader

constructor(option: ApiType) {
    // ...
    // 添加loadding
    this.instance.interceptors.request.use((config: AxiosRequestConfig) => {
        if (this.loading) {
            Toast.show({
                icon: 'loading',
                content: '加载中…',
                duration: 0
            });
        }
        return config;
    });
    this.instance.interceptors.response.use((res: AxiosResponse) => {
        if (this.loading) {
            Toast.clear();
        }
        return res;
    });
}

Внимательные студенты могут обнаружить, что нет конфликта с настроенным в прошлый раз перехватчиком?

Будет ли функция входящего перехватчика использоваться следующимиloadingфункция для переопределения?

Ответ - нет,axiosВнутри функции, которую мы передали, передается массив. При выполнении он будет выполняться в том порядке, в котором был передан. (не операция слияния)

// lib/core/InterceptorManager.js
// https://github.com/axios/axios/blob/master/lib/core/InterceptorManager.js

function InterceptorManager() {
  // 定义了一个存放拦截器的数组
  this.handlers = [];
}

// 依照传入的顺序进行执行指定的函数内容
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
};

Мы инкапсулировали базовую конфигурацию сети, а затем обработали данные, возвращаемые сетевым запросом.

Определите тип данных, возвращаемый интерфейсом

созданный ранееtypes/index.tsэкспортировать одинResponseDataTypeТип (этот шаг определяется в соответствии с данными, возвращаемыми бэкендом)

export interface ResponseDataType<Data = any> {
    body: Data,
    description: string,
    status: number
}

axiosпри условииrequestметод, эта функция может передавать указанный тип, и этот тип возвращается сетевым запросом в это время.

мы вApiЗатем инкапсулируйте этот метод в классе, а также передайте возвращаемый тип данных и соответствующую конфигурацию интерфейса и верните соответствующийPromiseобъект.

request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
export default class Api {
    // 添加数据类型约束
    async request<T>(config: AxiosRequestConfig<T>): Promise<T> {
        return this.instance.request<ResponseDataType, T>(config);
    }
}
Использование классов API

существуетapi/index.tsимпортировать вApiтак же какApiTypeинтерфейс.

import Api, {ApiType} from '../request';
Настроить переменные среды

Обычно мы будем различать среду разработки и среду генерации в проекте, чтобы уменьшить переключение адресов в процессе разработки.srcСоздал в корневом каталоге.env.development,.env.production,env.d.tsдокумент

По умолчанию сервер разработки (devкоманда) запуститьdevelopment(разработка) режиме, в то время какbuildтак же какserveКоманда выполняется вproduction(производственный) режим.

// .env.development 
VITE_APP_URL=请求的API地址前缀
// .env.production
VITE_APP_URL=请求的API地址前缀
// env.d.ts
interface ImportMetaEnv {
    readonly VITE_APP_URL: string
}

Получите переменную среды и экспортируйте ее.

// utilis/index.ts
// 获取变量中的环境
export const BASE_URL = import.meta.env.VITE_APP_URL;

Определите, чтобыApi Classвходящие данные параметра

// api/index.ts
import {BASE_URL} from 'utils';
const option: ApiType = {
    baseURL: BASE_URL,
    timeout: 5000,
};

передать параметры вApi Class, а затем мы вызываем его напрямую, когда запрашиваем данныеApiInstanceсерединаrequestметод.

const ApiInstance = new Api(option);
Создайте метод запроса сетевых данных

существуетtypes/index.tsопределено вswiperТип данных, возвращаемый интерфейсом

// types/index.ts
export interface SwiperDataType {
    alt: string
    id: number
    imgSrc: string
}

существуетapi/index.tsМетод, определенный в запросе, который вызывается внутриrequestметод и передать тип.

// api/index.ts
export const getSwiper = () => {
    return ApiInstance.request<SwiperDataType[]>({
        url: /swiper,
        method: 'get'
    });
};

использовать на страницеgetSwiperметод.

// 引入getSwiper方法
import {getSwiper} from '../../network/api';
// 获取请求回来的数据
const swiperData: SwiperDataType[] = await getSwiper();

наконец

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