«Это третий день моего участия в первом испытании обновлений 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
новое в проекте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. В будущем я также добавлю некоторую функциональную инкапсуляцию в сетевой запрос этой версии.