Browse Source

feat: 优化http模块

v7
谢亚昕 4 months ago
parent
commit
217b080b41
  1. 3
      public/data.json
  2. 6
      src/plugins/http/base.ts
  3. 33
      src/plugins/http/index.ts
  4. 0
      src/plugins/http/plugins.ts
  5. 97
      src/plugins/http/plugins/Loading.ts
  6. 36
      src/plugins/http/plugins/Repeat.ts
  7. 29
      src/plugins/request.ts
  8. 22
      src/views/Home/index.tsx

3
public/data.json

@ -0,0 +1,3 @@
{
"aa": "1231"
}

6
src/plugins/http/base.ts

@ -27,7 +27,7 @@ export class Plugin implements IPlugin {
export abstract class PluginsManager {
static plugins: IPlugin[] = []
static use(plugin: IPlugin) {
static use(plugin: IPlugin | IPlugin[]) {
if (Array.isArray(plugin)) {
PluginsManager.plugins = this.plugins.concat(plugin)
} else PluginsManager.plugins.push(plugin)
@ -75,7 +75,7 @@ export abstract class httpBase extends PluginsManager {
}, error => {
const argu = { error }
this.callPlugin("beforeRequestError", argu)
return Promise.reject(error)
return Promise.reject(argu.error)
})
this.responseInterceptorId = this.instance.interceptors.response.use(response => {
const argu = { response }
@ -84,7 +84,7 @@ export abstract class httpBase extends PluginsManager {
}, (error) => {
const argu = { error }
this.callPlugin("beforeResponseError", argu)
return Promise.reject(error)
return Promise.reject(argu.error)
})
return this.instance
}

33
src/plugins/http/index.ts

@ -1,10 +1,16 @@
import { httpBase, IPlugin } from "./base"
import { CreateAxiosDefaults, AxiosRequestConfig, AxiosResponse } from "axios"
import { RepeatPlugin } from "./Repeat"
const FlyPoll: Map<string, any> = new Map()
class Fly extends httpBase {
static disposeAll() {
if (FlyPoll.size) {
Array.from(FlyPoll.keys()).forEach(v => {
Fly.dispose(v)
})
}
}
static dispose(name: string) {
if (FlyPoll.has(name)) {
const instance = FlyPoll.get(name)
@ -23,7 +29,9 @@ class Fly extends httpBase {
static invoke(name?: string): Fly {
if (!name) name = "$defalut"
const instance = FlyPoll.get(name)
if (!instance) throw new Error("未初始化此实例")
if (!instance) {
throw new Error("未初始化此实例")
}
return instance
}
@ -44,7 +52,7 @@ class Fly extends httpBase {
}
default: CreateAxiosDefaults = {
// baseURL: process.env.VUE_APP_BASEURL,
baseURL: import.meta.env.BASE_URL,
timeout: 10000,
headers: {
'Content-Type': Fly.contentType.FORM,
@ -87,22 +95,3 @@ class Fly extends httpBase {
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)

0
src/plugins/http/plugins.ts

97
src/plugins/http/plugins/Loading.ts

@ -0,0 +1,97 @@
import { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"
import { IPlugin } from "../base"
import { Id, toast } from "react-toastify"
declare module "axios" {
interface AxiosRequestConfig {
loadingAttrs?: Partial<IConfig>
}
}
type AxiosRequestConfigPlus = AxiosRequestConfig & {
$loading_ToastID?: Id
$loading_CreateTimeStamp?: number
$loading_ToastShowFn?: () => void
$loading_ToastHideFn?: () => void
}
interface IConfig {
text: string
min: number
minShow: boolean
}
// 取消重复请求
export class LoadingPlugin implements IPlugin {
static name: string = "loading"
name: string = "loading"
defaultConfig: IConfig = {
text: "加载中",
min: 1000,
minShow: false,
}
config: IConfig = JSON.parse(JSON.stringify(this.defaultConfig))
constructor(config: Partial<IConfig> = {}) {
; (Object.keys(config) as (keyof IConfig)[]).forEach(v => {
if (config[v]) {
(this.config as any)[v] = config[v]
}
})
}
getConfig<T extends keyof IConfig>(key: T, config: Partial<IConfig> = {}) {
const keys = Object.keys(config) as T[]
for (let i = 0; i < keys.length; i++) {
const v = keys[i];
if (v === key) {
return (config as IConfig)[key]
}
}
return this.config[key]
}
beforeRequestConfig({ config }: { config: AxiosRequestConfigPlus }) {
const text = this.getConfig("text", config.loadingAttrs)
config.$loading_ToastShowFn = () => {
const id = toast.loading(text)
config.$loading_CreateTimeStamp = new Date().getTime()
config.$loading_ToastID = id
}
if (!this.getConfig("minShow", config.loadingAttrs)) {
config.$loading_ToastShowFn()
delete config.$loading_ToastShowFn
}
}
beforeResponse({ response }: { response: AxiosResponse }) {
const { config } = response as { config: AxiosRequestConfigPlus }
if (!this.getConfig("minShow", config.loadingAttrs)) {
const min = this.getConfig("min", config.loadingAttrs)
if (config.$loading_ToastID) {
const [ToastID, showTimeStamp] = [config.$loading_ToastID, config.$loading_CreateTimeStamp]
const oft = new Date().getTime() - showTimeStamp!
if (oft < min) {
setTimeout(() => {
toast.dismiss(ToastID)
}, min - oft);
} else {
toast.dismiss(ToastID)
}
delete config.$loading_ToastID
delete config.$loading_CreateTimeStamp
}
} else {
}
}
beforeResponseError(argu: { error: AxiosError }) {
const requestConfig = argu.error.config! as AxiosRequestConfigPlus
if (requestConfig.$loading_ToastID) {
const [ToastID] = requestConfig.$loading_ToastID.split("$")
toast.dismiss(ToastID)
delete requestConfig.$loading_ToastID
}
}
}

36
src/plugins/http/Repeat.ts → src/plugins/http/plugins/Repeat.ts

@ -1,5 +1,5 @@
import { isCancel, AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"
import { IPlugin } from "./base"
import { IPlugin } from "../base"
declare module "axios" {
interface AxiosRequestConfig {
@ -7,14 +7,33 @@ declare module "axios" {
* RepeatPlugin插件的clearPendingPool清除
*/
global?: boolean
cancelAttrs?: Partial<IConfig>
}
}
interface IConfig {
text: string
}
// 取消重复请求
export class RepeatPlugin implements IPlugin {
static name: string = "repeat"
name: string = "repeat"
pendingPool = new Map()
defaultConfig: IConfig = {
text: "${url}请求取消"
}
config: IConfig = JSON.parse(JSON.stringify(this.defaultConfig))
constructor(config: Partial<IConfig> = {}) {
; (Object.keys(config) as (keyof IConfig)[]).forEach(v => {
if (config[v]) {
this.config[v] = config[v]
}
})
}
clearPendingPool(whiteList: string[] = []) {
if (!this.pendingPool.size) return
const pendingUrlList = Array.from(this.pendingPool.keys()).filter(url => !whiteList.includes(url))
@ -22,7 +41,7 @@ export class RepeatPlugin implements IPlugin {
pendingUrlList.forEach(pendingUrl => {
if (!this.pendingPool.get(pendingUrl).global) {
this.pendingPool.get(pendingUrl).cancelFn(`${pendingUrl} 请求取消`)
this.pendingPool.get(pendingUrl).cancelFn(`${this.config.text}`.replace(/\$\{(.*?)}/g, (_, t) => t === "url" ? pendingUrl : ''))
this.pendingPool.delete(pendingUrl)
}
})
@ -40,12 +59,13 @@ export class RepeatPlugin implements IPlugin {
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}' 被取消`)
beforeResponseError(argu: { error: AxiosError }) {
const requestConfig = argu.error.config!
if (!isCancel(argu.error)) this.pendingPool.delete(requestConfig.url)
if (!argu.error) return
if (isCancel(argu.error)) {
argu.error.message = (requestConfig.signal as AbortSignal)?.reason || argu.error.message || `请求'${requestConfig.url}' 被取消`
return
}
}
}

29
src/plugins/request.ts

@ -1,14 +1,39 @@
import { Fly } from "./http"
import { RepeatPlugin } from "./http/Repeat"
import { RepeatPlugin } from "./http/plugins/Repeat"
import { LoadingPlugin } from "./http/plugins/Loading"
const allPlugin = {
loading: new LoadingPlugin(),
repeat: new RepeatPlugin(),
}
Fly.use([allPlugin.loading])
Fly.init("$defalut", {
timeout: 10000,
}, [new RepeatPlugin()])
}, [allPlugin.repeat])
Fly.init("empty", {
timeout: 10000,
})
// Fly.disposeAll()
// https://api.52vmy.cn/
// Fly.invoke().request({ method: "get", url: "http://localhost:5173/asd", 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)
export {
Fly

22
src/views/Home/index.tsx

@ -2,21 +2,27 @@ import withPage from "@/base/withPage"
import { Hero } from "@/ui/Hero"
import { Button } from "@blueprintjs/core"
import { ReactNode, useCallback } from "react"
// import { Fly } from "@/plugins/request"
import { Fly } from "@/plugins/request"
import { toast } from "react-toastify"
interface IProps {
// children: ReactNode
}
export default function Home({}: IProps) {
export default function Home({ }: IProps) {
const onClick = useCallback(() => {
toast.success("Wow so easy !")
// Fly.invoke().request<{a: number}>({
// method: "get",
// url: "http://api.juheapi.com/japi/toh",
// global: false
// }).then((res) => console.log(res.data)).catch(console.log)
// toast.success("Wow so easy !")
Fly.invoke().request<{ a: number }>({
method: "get",
url: "/data.json",
global: false,
loadingAttrs: {
text: "aasd",
minShow: false
}
}).then((res) => console.log(res.data)).catch(err => {
toast.error(err.stack)
})
}, [])
return (

Loading…
Cancel
Save