From ce83a9c30b8289c302b1314bd7f4401701eaffe3 Mon Sep 17 00:00:00 2001 From: ray_wuhao <443547225@qq.com> Date: Mon, 27 Feb 2023 17:19:19 +0800 Subject: [PATCH] =?UTF-8?q?axios=E5=8F=96=E6=B6=88=E5=99=A8=E9=87=8D?= =?UTF-8?q?=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/axios/canceler.ts | 83 +++++++++++++++++++++++++++++++++++++++ src/axios/index.ts | 38 +++++++++--------- src/axios/instance.ts | 24 +++++++++++ src/views/axios/index.tsx | 1 - 4 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 src/axios/canceler.ts diff --git a/src/axios/canceler.ts b/src/axios/canceler.ts new file mode 100644 index 00000000..b753d243 --- /dev/null +++ b/src/axios/canceler.ts @@ -0,0 +1,83 @@ +/** + * + * @author Ray + * + * @date 2023-02-27 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +/** + * + * 自动取消重复请求 + * + * 可以根据自己项目进行定制化配置 + */ + +import type { AxiosRequestConfig } from 'axios' + +export default class RequestCanceler { + pendingRequest: Map + + constructor() { + this.pendingRequest = new Map() + } + + /** + * + * @param config 请求体 config + * @returns 返回当前请求拼接 key + * + * @remark 将当前请求 config 生成 request key + */ + generateRequestKey(config: AxiosRequestConfig): string { + const { method, url } = config + + return [ + url || '', + method || '', + JSON.stringify(config.params), + JSON.stringify(config.data), + ].join('&') + } + + /** + * + * @param config axios request config + * + * @remark 给请求体添加 signal 属性, 用于取消请求 + */ + addPendingRequest(config: AxiosRequestConfig) { + const requestKey: string = this.generateRequestKey(config) + + if (!this.pendingRequest.has(requestKey)) { + const controller = new AbortController() + + config.signal = controller.signal + this.pendingRequest.set(requestKey, controller) + } else { + // 如果已经有该 key 则重新挂载 signal + config.signal = ( + this.pendingRequest.get(requestKey) as AbortController + ).signal + } + } + + /** + * + * @param config axios request config + * + * @remark 取消该请求, 并且清除 map 中对应 generateRequestKey value + */ + removePendingRequest(config: AxiosRequestConfig) { + const requestKey = this.generateRequestKey(config) + + if (this.pendingRequest.has(requestKey)) { + ;(this.pendingRequest.get(requestKey) as AbortController).abort() + + this.pendingRequest.delete(requestKey) + } + } +} diff --git a/src/axios/index.ts b/src/axios/index.ts index 3775e1d6..131db6bf 100644 --- a/src/axios/index.ts +++ b/src/axios/index.ts @@ -1,28 +1,30 @@ +/** + * + * @author Ray + * + * @date 2023-02-27 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +/** + * + * 为什么要写这个文件在这儿多此一举呢: + * - 考虑到可能需要什么特殊操作, 所以提前写在这儿了 + * - 可以个性化配置一些东西, 所有配置都会合并到 axios instance config 中 + * - 当然, 你也可以直接使用 instance + */ + import request from './instance' import type { AxiosRequestConfig } from 'axios' -let controller: AbortController - const useRequest = (config: AxiosRequestConfig) => { - controller && controller.abort() // 调用控制器, 取消请求 - - controller = new AbortController() // 实例化控制器对象(可以中止一个或多个 `Web` 请求) - - const cfg = Object.assign({}, config, { - signal: controller.signal, // 取消请求信号 - }) + const cfg = Object.assign({}, config, {}) return request(cfg) } export default useRequest - -/** - * - * 使用 `axios` 实例 - * - * 可以自动取消重复请求部分 - * - * 使用该方法进行二次的请求封装, 然后可以使用 `import { xxx } from '@use-api/xxx' 导入 - */ diff --git a/src/axios/instance.ts b/src/axios/instance.ts index fdd20595..58e815b0 100644 --- a/src/axios/instance.ts +++ b/src/axios/instance.ts @@ -1,9 +1,23 @@ +/** + * + * @author Ray + * + * @date 2022-11-18 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + import axios from 'axios' import { useDetermineEnv } from '@use-utils/hook' +import RequestCanceler from './canceler' import type { RawAxiosRequestHeaders, AxiosRequestConfig } from 'axios' import type { RequestHeaderOptions } from './type' +const canceler = new RequestCanceler() + /** * * @param instance axios instance @@ -26,6 +40,9 @@ const server = axios.create({ baseURL: '', // `import.meta.env`, withCredentials: false, // 是否允许跨域携带 `cookie` timeout: 5 * 1000, + headers: { + 'Content-Type': 'application/json', + }, }) server.interceptors.request.use( @@ -47,6 +64,9 @@ server.interceptors.request.use( }, ]) // 自定义请求头 + canceler.removePendingRequest(request) // 检查是否存在重复请求, 若存在则取消已发的请求 + canceler.addPendingRequest(request) // 把当前的请求信息添加到 pendingRequest 表中 + return request }, (error) => { @@ -56,11 +76,15 @@ server.interceptors.request.use( server.interceptors.response.use( (response) => { + canceler.removePendingRequest(response.config) + const { data } = response return Promise.resolve(data) }, (error) => { + canceler.removePendingRequest(error.config || {}) + return Promise.reject(error) }, ) diff --git a/src/views/axios/index.tsx b/src/views/axios/index.tsx index 6716841e..cc651cfd 100644 --- a/src/views/axios/index.tsx +++ b/src/views/axios/index.tsx @@ -63,7 +63,6 @@ const Axios = defineComponent({ 基于 axios 封装, 能够自动取消连续请求, 避免重复渲染造成问题. - 可在该示例中测试, 并且打开控制台的网络选项卡查看