Browse Source

feat: 增加axios

main
谢亚昕 5 months ago
parent
commit
c1745e836a
  1. 48
      src/plugins/http/Repeat.ts
  2. 130
      src/plugins/http/base.ts
  3. 112
      src/plugins/http/index.ts
  4. 15
      src/plugins/request.ts
  5. 2
      src/ui/Register/Register.tsx
  6. 17
      src/views/Home/index.tsx

48
src/plugins/http/Repeat.ts

@ -0,0 +1,48 @@
import { isCancel, AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"
import { IPlugin } from "./base"
declare module "axios" {
interface AxiosRequestConfig {
global?: boolean
}
}
// 取消重复请求
export class RepeatPlugin implements IPlugin {
static name: string = "repeat"
name: string = "repeat"
pendingPool = new Map()
clearPendingPool(whiteList: string[] = []) {
if (!this.pendingPool.size) return
const pendingUrlList = Array.from(this.pendingPool.keys()).filter(url => !whiteList.includes(url))
if (!pendingUrlList.length) return
pendingUrlList.forEach(pendingUrl => {
if (!this.pendingPool.get(pendingUrl).global) {
this.pendingPool.get(pendingUrl).cancelFn(`${pendingUrl} 请求取消`)
this.pendingPool.delete(pendingUrl)
}
})
return pendingUrlList
}
beforeRequestConfig({ config }: { config: AxiosRequestConfig }) {
const controller = new AbortController();
config.signal = controller.signal
const cancelFn = (reason?: string) => controller.abort(reason)
this.pendingPool.has(config.url)
? cancelFn(`${config.url} 请求重复`)
: this.pendingPool.set(config.url, { cancelFn, global: config.global })
}
beforeResponse({ response }: { response: AxiosResponse }) {
const { config } = response
this.pendingPool.delete(config.url)
}
beforeResponseError({ error }: { error: AxiosError }) {
const requestConfig = error.config!
if (!isCancel(error)) this.pendingPool.delete(requestConfig.url)
if (!error) return
if (isCancel(error)) {
throw new Error((requestConfig.signal as AbortSignal)?.reason || error.message || `请求'${requestConfig.url}' 被取消`)
}
}
}

130
src/plugins/http/base.ts

@ -1,62 +1,104 @@
import axios, { AxiosInstance, CreateAxiosDefaults } from "axios"; import axios, { AxiosInstance, CreateAxiosDefaults } from "axios";
import { deepAssign } from "./utils"; import { deepAssign } from "./utils";
export const enum IHttpMethod { export interface IPlugin {
GET = "GET", name: string
POST = "POST", created?(...argus: any[]): void
DELETE = "DELETE", beforeCreate?(...argus: any[]): void
PUT = "PUT", beforeDestory?(...argus: any[]): void
beforeRequestConfig?(...argus: any[]): void
beforeRequestError?(...argus: any[]): void
beforeResponse?(...argus: any[]): void
beforeResponseError?(...argus: any[]): void
destory?(...argus: any[]): void
} }
export enum IHttpContentType { export class Plugin implements IPlugin {
FORM = "application/x-www-form-urlencoded", name: string = ""
JSON = "application/json", created() { }
} beforeCreate() { }
beforeDestory() { }
class Plugin { beforeRequestConfig() { }
beforeInit() { } beforeRequestError() { }
beforeResponse() { }
beforeResponseError() { }
destory() { }
} }
export abstract class PluginsManager { export abstract class PluginsManager {
static #plugins: Plugin[] = [] static plugins: IPlugin[] = []
static use(plugin: Plugin) { static use(plugin: IPlugin) {
this.#plugins.push(plugin) if (Array.isArray(plugin)) {
} PluginsManager.plugins = this.plugins.concat(plugin)
} else PluginsManager.plugins.push(plugin)
}
callPlugin(key: keyof Plugin, ...argus: any[]) { plugins: IPlugin[] = []
PluginsManager.#plugins.forEach(p => p[key].apply(this, argus as [])) use(plugin: IPlugin) {
if (Array.isArray(plugin)) {
this.plugins = this.plugins.concat(plugin)
} else this.plugins.push(plugin)
}
callPluginByName<T>(name: string, key: keyof T, ...argus: any[]) {
const array = [...PluginsManager.plugins, ...this.plugins]
for (let i = 0; i < array.length; i++) {
let p = array[i]
if (!p.name) continue
if (name === p.name) {
const fn = (p as T)[key]
typeof fn === "function" && fn.apply(p, argus)
}
} }
}
callPlugin(key: keyof IPlugin, ...argus: any[]) {
const array = [...PluginsManager.plugins, ...this.plugins]
for (let i = 0; i < array.length; i++) {
let p = array[i]
const fn = p[key]
typeof fn === "function" && fn.apply(p, argus)
}
}
} }
export abstract class httpBase extends PluginsManager { export abstract class httpBase extends PluginsManager {
static get method() {
return {
GET: "GET",
POST: "POST",
DELETE: "DELETE",
PUT: "PUT",
}
}
static get contentType() { instance: AxiosInstance | null = null
return { requestInterceptorId: null | number = null
FORM: "application/x-www-form-urlencoded", responseInterceptorId: null | number = null
JSON: "application/json",
}
}
initDefaultConfig: CreateAxiosDefaults = {
baseURL: process.env.VUE_APP_BASEURL,
timeout: 10000,
headers: {
'Content-Type': httpBase.contentType.FORM,
}
}
instance: AxiosInstance | null = null create<T>(config?: CreateAxiosDefaults<T>) {
this.instance = axios.create(deepAssign<CreateAxiosDefaults<T>>(axios.defaults, config ?? {}))
this.requestInterceptorId = this.instance.interceptors.request.use(config => {
const argu = { config }
this.callPlugin("beforeRequestConfig", argu)
return argu.config
}, error => {
const argu = { error }
this.callPlugin("beforeRequestError", argu)
return Promise.reject(error)
})
this.responseInterceptorId = this.instance.interceptors.response.use(response => {
const argu = { response }
this.callPlugin("beforeResponse", argu)
return Promise.resolve(argu.response)
}, (error) => {
const argu = { error }
this.callPlugin("beforeResponseError", argu)
return Promise.reject(error)
})
return this.instance
}
create<T>(config?: CreateAxiosDefaults<T>) { destory() {
this.instance = axios.create(deepAssign<CreateAxiosDefaults<T>>(axios.defaults, this.initDefaultConfig, config ?? {})) // 清理一些事务
return this.instance if (this.requestInterceptorId) {
this.instance?.interceptors.request.eject(this.requestInterceptorId)
}
if (this.responseInterceptorId) {
this.instance?.interceptors.response.eject(this.responseInterceptorId)
} }
httpBase.plugins = []
this.plugins = []
this.instance = null
}
} }

112
src/plugins/http/index.ts

@ -1,37 +1,99 @@
import { httpBase, PluginsManager } from "./base" import { httpBase, IPlugin } from "./base"
import { CreateAxiosDefaults, AxiosRequestConfig, AxiosResponse } from "axios"
export const enum IHttpMethod { import { RepeatPlugin } from "./Repeat"
GET = "GET",
POST = "POST",
DELETE = "DELETE",
PUT = "PUT",
}
export enum IHttpContentType {
FORM = "application/x-www-form-urlencoded",
JSON = "application/json",
}
const FlyPoll: Map<string, any> = new Map() const FlyPoll: Map<string, any> = new Map()
class Fly extends httpBase { class Fly extends httpBase {
constructor() { static dispose(name: string) {
super() if (FlyPoll.has(name)) {
this.Init() const instance = FlyPoll.get(name)
instance.#destory()
FlyPoll.delete(name)
}
} }
static get(name?: string): Fly { static init(name: string, config: any, plugins: IPlugin[] = []) {
if (!name) name = "$defalt"
if (!FlyPoll.has(name)) { if (!FlyPoll.has(name)) {
FlyPoll.set(name, new Fly()) const instance = new Fly(name)
FlyPoll.set(name, instance)
instance.plugins = plugins
instance.#init(config || instance.default)
}
}
static invoke(name?: string): Fly {
if (!name) name = "$defalut"
const instance = FlyPoll.get(name)
if (!instance) throw new Error("未初始化此实例")
return instance
}
static get method() {
return {
GET: "GET",
POST: "POST",
DELETE: "DELETE",
PUT: "PUT",
}
}
static get contentType() {
return {
FORM: "application/x-www-form-urlencoded",
JSON: "application/json",
}
}
default: CreateAxiosDefaults = {
// baseURL: process.env.VUE_APP_BASEURL,
timeout: 10000,
headers: {
'Content-Type': Fly.contentType.FORM,
} }
return FlyPoll.get(name)
} }
Init() {
this.callPlugin("beforeInit") name: string
this.create()
constructor(name: string) {
super()
this.name = name
}
get(...argus: [url: string, config?: AxiosRequestConfig<unknown> | undefined]) {
return this.instance!.get.apply(this.instance, argus ?? []) as Promise<AxiosResponse>
}
request(...argus: [config: AxiosRequestConfig<unknown>]): Promise<AxiosResponse> {
return this.instance!.request.apply(this.instance, argus) as Promise<AxiosResponse>
}
#init(config: AxiosRequestConfig) {
this.callPlugin("beforeCreate")
this.create(config)
this.callPlugin("created")
}
#destory() {
this.callPlugin("beforeDestory")
this.destory()
this.callPlugin("destory")
} }
} }
Fly.get() export {
Fly
}
// https://api.52vmy.cn/
// Fly.invoke().request({ method: "get", url: "http://localhost:5173/", global: false }).then((res) => console.log(res.data)).catch(console.log)
// Fly.invoke().request({ method: "get", url: "http://localhost:5173/", global: true }).then((res) => console.log(res.data)).catch(console.log)
// Fly.invoke().request({ method: "get", url: "https://api.52vmy.cn/api/wl/word/bing/tu", global: true }).then((res) => console.log(res.data)).catch(console.log)
// Fly.invoke().request({ method: "get", url: "https://api.52vmy.cn/api/wl/word/bing/tu", global: true }).then((res) => console.log(res.data)).catch(console.log)
// Fly.invoke("empty").request({ method: "get", url: "https://api.52vmy.cn/api/wl/yan/bay" }).then((res) => console.log(res.data)).catch(console.log)
// Fly.invoke("empty").request({ method: "get", url: "https://api.52vmy.cn/api/img/tu/girl" }).then((res) => console.log(res.data)).catch(console.log)
// setTimeout(()=>{
// // Fly.invoke("empty").request({method: "get", url: "https://api.52vmy.cn/api/img/tu/man"}).then((res)=>console.log(res.data)).catch(console.log)
// Fly.invoke().callPluginByName<RepeatPlugin>(RepeatPlugin.name, "clearPendingPool")
// }, 0)

15
src/plugins/request.ts

@ -0,0 +1,15 @@
import { Fly } from "./http"
import { RepeatPlugin } from "./http/Repeat"
Fly.init("$defalut", {
timeout: 10000,
}, [new RepeatPlugin()])
Fly.init("empty", {
timeout: 10000,
})
export {
Fly
}

2
src/ui/Register/Register.tsx

@ -60,7 +60,7 @@ export function Register({ onSuccess, children }: IProps) {
return ( return (
<> <>
<form onSubmitCapture={clickRegister}> <form onSubmit={()=>false} onSubmitCapture={clickRegister}>
<WrapperComp> <WrapperComp>
<FormGroup helperText="请输入一个可用的邮箱" label="邮箱" labelFor="text-input" labelInfo="(必须)"> <FormGroup helperText="请输入一个可用的邮箱" label="邮箱" labelFor="text-input" labelInfo="(必须)">
<InputGroup <InputGroup

17
src/views/Home/index.tsx

@ -1,18 +1,29 @@
import withPage from "@/base/withPage" import withPage from "@/base/withPage"
import { Hero } from "@/ui/Hero" import { Hero } from "@/ui/Hero"
import { ReactNode } from "react" import { Button } from "@blueprintjs/core"
import { ReactNode, useCallback } from "react"
import { Fly } from "@/plugins/request"
interface IProps { interface IProps {
children: ReactNode children: ReactNode
} }
export default withPage(function Project({}: IProps) { export default withPage(function Project({ }: IProps) {
const onClick = useCallback(() => {
Fly.invoke().request({
method: "get",
url: "https://api.52vmy.cn/api/wl/word/bing/tu",
global: false
}).then((res) => console.log(res.data)).catch(console.log)
}, [])
return ( return (
<> <>
<Hero></Hero> <Hero></Hero>
<div className="container"> <div className="container">
<Button onClick={onClick}></Button>
</div> </div>
</> </>
) )

Loading…
Cancel
Save