Axios 封装:处理重复调用与内容覆盖问题

问题描述&背景

  • 下拉选择框,支持搜索,搜索时携带参数调用接口并更新下拉选项
  • 下拉选择连续进行多次搜索,先请求但响应时间长的返回值会覆盖后请求但响应时间短的
  • 举例:
    • 搜索后先清空选项,再输入内容进行搜索。清空后查询全量数据接口响应时间更长,覆盖搜索过滤后的数据

问题分析

  • 连续多次请求导致问题
    • 通过防抖debounce函数,限制短期内无法重复调用接口 - 使用 lodashdebounce 函数实现
    • 若接口响应时间相差较大,仍会有覆盖问题,需要结合以下方法
  • 接口响应慢导致问题
    • 优化接口,如减少后端非必要属性的计算,提高响应速度 - 后端优化
  • 接口调用重复问题
    • 通过一些方法保证最后显示的数据为最晚发起的那个请求返回的值,方案见下文

方案选择及具体实施

  1. 在前一个请求响应前,阻止发起下一个请求(问题中通过禁用选择框 disabled=true 实现),避免返回值覆盖
    • 实现方法
      • 接口请求时将组件的disabledloading均设置为true
      • 返回后设置为false
    • 优点
      • 可以减少接口调用,防止返回值相互覆盖
    • 缺点
      • 禁用选择框会让其失去焦点,用户需要再次点击输入
      • 禁用状态的切换使得操作不连贯,用户有明显的感知,体验下降
      • 需要操作页面元素,需要额外代码
  2. 发送请求时,通过给每次请求添加一个序列号或时间戳,然后在处理响应时进行匹配,确保每次返回的结果与其对应
    • 实现方法
      • 发送请求时生成唯一的标识符(时间戳)
      • 处理响应时保存该标识符
      • 匹配标识符更新数据
    • 优点
      • 可以找出最新的请求赋值,保证数据为最后请求的
    • 缺点
      • 需要多次更新使用的数据
      • 需要生成标识并用额外的变量储存标识,逻辑改动较大
      • 没有实际减少或取消无效的请求,占用资源多
  3. 发起新的请求时取消尚未完成的请求 ⭐️
    • 运用技术
      • axios的取消请求:axios取消请求
      • 项目使用的axios版本为0.21.1,使用CancelToken
      • 更高版本使用AbortControllerAbortController:AbortController() 构造函数

AbortController实现方法

const controller = new AbortController();
const {data: { data },
} = await this.$http.get('/api/v1/xxx'params,signal: controller.signal
})
// 取消请求
controller.abort()

CancelToken实现方法

  • 在data中定义cancelToken用于保存当前请求token
data() {return {...cancelToken: null,}
},
  • 在查询方法中进行如下配置
// 防抖
searchOptions: debounce(async function (searchString) {// 取消上一次的请求if (this.cancelToken) {this.cancelToken.cancel()}// 创建 cancelTokenthis.cancelToken = axios.CancelToken.source()this.loading = trueconst params = {...}const {data: { data },} = await this.$http.get('/api/v1/xxx'params,cancelToken: this.cancelToken.token, // 请求时传入token})// 数据处理...this.loading = false// 清除cancelTokenthis.cancelToken = null},300,{leading: false,trailing: true,}
),
  1. 使用第三方插件进行优化 ⭐️
    • 插件名称:axios-extensions
    • 功能:缓存请求结果,节流请求,请求重试
    • 缓存请求:cacheAdapterEnhancer
      cacheAdapterEnhancer(axios.defaults.adapter, option)
      • option 对象,可选
        • enabledByDefault:是否默认缓存,Boolean类型, 默认是true(缓存), false(不缓存)
        • cacheFlag:是否通过flag方式缓存,字符串类型, 只有flag一样才会缓存,flag不对或者没设置的都不会缓存。
        • defaultCache:可以配置maxAge(缓存有效时间, 毫秒单位),默认是5分钟,max(支持缓存的请求的最大个数),默认是100个
    • 完整代码 🚀
import axios from 'axios'
import { Cache, cacheAdapterEnhancer } from 'axios-extensions'const request = axios.create({baseURL: process.env.BASE_URL,adapter: cacheAdapterEnhancer(axios.defaults.adapter, {defaultCache: new Cache({ maxAge: 2000, max: 100 }),}),
})
  • 扩展:
    • 节流请求:throttleAdapterEnhancer
      throttleAdapterEnhancer(adapter, options)
      • option 对象,可选
        • threshold:限制请求调用的毫秒数,数字类型, 默认是1000
        • cache:可以配置 max(节流请求的最大个数),默认是100个
    • 请求重试:retryAdapterEnhancer
      retryAdapterEnhancer(adapter, options)
      • option 对象,可选
        • times:重试的次数,Number类型, 默认是2,请求失败后会重试2次。

优化效果

  • 杜绝先发起的请求结果覆盖旧发起的请求结果的情况
  • 新的请求发起,取消当前进行中的请求,减少无用的请求调用
  • 无需修改其他的业务逻辑,无需引入更多变量记录请求状态
  • 用户没有感知,不会增加额外的用户操作

功能封装

  • 封装一个基于 axios 的 HTTP 请求管理类:http.ts 🚀
import axios, { AxiosRequestConfig, CancelTokenSource } from 'axios'// 引入请求函数 包含一些请求拦截器或其他设置
import { request } from './axiosConfig' // 枚举 指定响应数据的格式(这里只举例1种返回体格式)
enum ResponseType {ResData, // 返回 res.data
}
// 完整的响应对象结构
interface ApiResponse<T> {data: {code: numbermessage: stringdata: T | null | undefined}status: numberheaders?: Record<string, string>config?: anyrequest?: any
}
// 异步请求的结果
type HttpResult<T> = Promise<T | ApiResponse<T> | any> // 扩展了 Axios 的请求配置,添加了两个自定义字段以支持请求取消功能
interface CustomAxiosRequestConfig extends AxiosRequestConfig {cancelPrevious?: boolean // 是否取消之前的请求cancelTokenId?: string // 保存取消请求tokenId
}class Http {// 存储 Http 类的实例,以便实现单例模式private static instancesMap: Map<string, Http> = new Map()// 存储与请求 URL 关联的取消令牌源,用于实现请求取消功能private static cancelTokenIdSourceMap: Map<string, CancelTokenSource> =new Map()private requestFunction: (config: AxiosRequestConfig) => Promise<any> // 请求方法private responseType: ResponseType = ResponseType.ResData // 相应数据格式类型// 构造函数-接收参数以配置请求函数、响应类型和公共URL前缀,同时初始化相关属性constructor({requestMethod = request,responseType = ResponseType.ResData,}: {requestMethod?: (config: AxiosRequestConfig) => Promise<any>}) {this.requestFunction = requestMethodthis.responseType = responseType}// 私有异步方法,用于执行 HTTP 请求,接受请求方法、URL 和配置private async createRequest<T>({method,url,config,}: {method: 'get' | 'post' | 'delete' | 'put'url: stringconfig: CustomAxiosRequestConfig}): HttpResult<T> {let source, cancelTokenIdif (config?.cancelPrevious) {// 取消之前的请求cancelTokenId = config?.cancelTokenId ?? this.getCancelTokenId(url)this.cancelPreviousRequest(cancelTokenId)// 创建新的取消令牌source = axios.CancelToken.source()}// 准备请求配置const requestConfig: AxiosRequestConfig = {...config,method,url,cancelToken: source?.token,}// 请求try {// 保存取消令牌if (cancelTokenId) Http.cancelTokenIdSourceMap.set(cancelTokenId, source)// 发起请求const res = await this.requestFunction(requestConfig)// 没有遇到重复请求-清空取消令牌if (cancelTokenId) Http.cancelTokenIdSourceMap.delete(cancelTokenId)// 返回响应值if (this.responseType === ResponseType.ResData) {return res.data as T} else {return res as ApiResponse<T>}} catch (error) { // 错误处理if (axios.isCancel(error)) {console.error('Request canceled', error.message)} else {if (cancelTokenId) Http.cancelTokenIdSourceMap.delete(cancelTokenId)console.error('Error:', error)}throw error}}private cancelPreviousRequest(cancelTokenId: string): void {const source = Http.cancelTokenIdSourceMap.get(cancelTokenId)source?.cancel(`Cancelled request ${cancelTokenId}`)}private getCancelTokenId(url: string): string {return url.split('?')[0] // 提取非 query 部分, 防止同一个get请求不同query时没取消}// 实现get方法public get<T>(url: string,config?: CustomAxiosRequestConfig): HttpResult<T> {return this.createRequest<T>({ method: 'get', url, config })}// 实现post方法public post<T>(url: string,data?: any,config?: CustomAxiosRequestConfig): HttpResult<T> {return this.createRequest<T>({method: 'post',url,config: { ...config, data },})}// 实现delete方法public delete<T>(url: string,config?: CustomAxiosRequestConfig): HttpResult<T> {return this.createRequest<T>({ method: 'delete', url, config })}// 实现put方法public put<T>(url: string,data?: any,config?: CustomAxiosRequestConfig): HttpResult<T> {return this.createRequest<T>({method: 'put',url,config: { ...config, data },})}// 单例// 该方法检查是否已经存在相同 ID 的实例,如果不存在,则创建一个新的实例并存储在 instancesMap 中。// 这样做的目的是减少同类实例的创建,确保在应用中使用的是同一个 Http 实例,从而管理配置和状态public static getInstance({requestMethod = request,responseType = ResponseType.ResData,instanceId = 'http',}: {requestMethod?: (config: AxiosRequestConfig) => Promise<any>responseType?: ResponseTypeinstanceId?: string}): Http {let instance = Http.instancesMap.get(instanceId)if (!instance) {instance = new Http({ requestMethod, responseType })Http.instancesMap.set(instanceId, instance)}return instance}
}// 导出实例
export const http = Http.getInstance({requestMethod: request,responseType: ResponseType.ResData,instanceId: 'http',
})
  • 补充:
// Axios 请求实例
const request = axios.create({baseURL: process.env.BASE_URL,adapter: cacheAdapterEnhancer(axios.defaults.adapter, {defaultCache: new Cache({ maxAge: 2000, max: 100 }),}),
})
  • 使用
await this.$http.post(`/xxx/xxx/${this.id}/xxx`,params
)

参考文档

来学习下axios的扩展插件1
来学习下axios的扩展插件2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/67294.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

openssl s_server源码剥离

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

51单片机 DS18B20温度储传感器

DS18B20温度传感器 64-BITROM&#xff1a;作为器件地址&#xff0c;用于总线通信的寻址&#xff0c;是唯一的&#xff0c;不可更改 SCRATCHPAD&#xff08;暂存器&#xff09;&#xff1a;用于总线的数据交互 EEPROM&#xff1a;用于保存温度触发阈值和配置参数 暂存器 单总线…

如何学习Transformer架构

Transformer架构自提出以来&#xff0c;在自然语言处理领域引发了革命性的变化。作为一种基于注意力机制的模型&#xff0c;Transformer解决了传统序列模型在并行化和长距离依赖方面的局限性。本文将探讨Transformer论文《Attention is All You Need》与Hugging Face Transform…

如何选择合适的服务器?服务器租赁市场趋势分析

服务器租赁市场概览 服务器租赁 market可以分为两种类型&#xff1a;按小时、按月和按年&#xff0c;每种模式都有其特点和适用场景&#xff0c;按小时租赁是最经济实惠的选择&#xff0c;适用于短期需求&#xff1b;按月租赁则适合中长期使用&#xff1b;而按年租赁则是最灵活…

[操作系统] 深入理解操作系统的概念及定位

概念 任何计算机系统都包含⼀个基本的程序集合&#xff0c;称为操作系统(OS)。 其核心功能如图片所示&#xff0c;包括&#xff1a; 内核 (Kernel)&#xff1a; 内核是操作系统的核心部分&#xff0c;被认为是狭义上的操作系统&#xff0c;直接与硬件打交道。负责进程管理、内…

Java并发编程——线程池(基础,使用,拒绝策略,命名,提交方式,状态)

我是一个计算机专业研0的学生卡蒙Camel&#x1f42b;&#x1f42b;&#x1f42b;&#xff08;刚保研&#xff09; 记录每天学习过程&#xff08;主要学习Java、python、人工智能&#xff09;&#xff0c;总结知识点&#xff08;内容来自&#xff1a;自我总结网上借鉴&#xff0…

nginx 配置代理,根据 不同的请求头进行转发至不同的代理

解决场景&#xff1a;下载发票的版式文件&#xff0c;第三方返回的是url链接地址&#xff0c;但是服务是部署在内网环境&#xff0c;无法访问互联网进行下载。此时需要进行走反向代理出去&#xff0c;如果按照已有套路&#xff0c;就是根据不同的访问前缀&#xff0c;跳转不同的…

设计一个流程来生成测试模型安全性的问题以及验证模型是否安全

要使用 Ollama 运行 llama3.3:70b 模型&#xff0c;并设计一个流程来生成测试模型安全性的问题以及验证模型是否安全&#xff0c;可以按照以下步骤进行设计和实现。整个过程包括环境配置、设计安全测试提示词、执行测试以及分析结果。以下是详细的步骤和指导&#xff1a; 1. 环…

iOS - TLS(线程本地存储)

从源码中&#xff0c;详细总结 TLS (Thread Local Storage) 的实现&#xff1a; 1. TLS 基本结构 // TLS 的基本结构 struct tls_data {pthread_key_t key; // 线程本地存储的键void (*destructor)(void *); // 清理函数 };// 自动释放池的 TLS class Autorelease…

docker在不删除容器的情况下修改端口映射

注意&#xff1a;必须先停止docker服务&#xff01;&#xff01;&#xff01;&#xff01; 1) 停止容器 2) 停止docker服务(systemctl stop docker) 3) 修改这个容器的hostconfig.json和config.v2.json文件中的端口 先查看容器id docker inspect jenkins 进入该目录 hostcon…

【js进阶】设计模式之单例模式的几种声明方式

单例模式&#xff0c;简言之就是一个类无论实例化多少次&#xff0c;最终都是同一个对象 原生js的几个辅助方式的实现 手写forEch,map,filter Array.prototype.MyForEach function (callback) {for (let i 0; i < this.length; i) {callback(this[i], i, this);} };con…

专题 - STM32

基础 基础知识 STM所有产品线&#xff08;列举型号&#xff09;&#xff1a; STM产品的3内核架构&#xff08;列举ARM芯片架构&#xff09;&#xff1a; STM32的3开发方式&#xff1a; STM32的5开发工具和套件&#xff1a; 若要在电脑上直接硬件级调试STM32设备&#xff0c;则…

-bash: /java: cannot execute binary file

在linux安装jdk报错 -bash: /java: cannot execute binary file 原因是jdk安装包和linux的不一致 程序员的面试宝典&#xff0c;一个免费的刷题平台

【MySQL】使用C语言链接

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;MySQL 目录 一&#xff1a;&#x1f525; MySQL connect &#x1f98b; Connector / C 使用&#x1f98b; mysql 接口介绍&#x1f98b; 完整代码样例 二&#xff1a;&#x1f525; 共勉 一&#…

平滑算法 效果比较

目录 高斯平滑 效果对比 移动平均效果比较: 高斯平滑 效果对比 右边两个参数是1.5 2 代码: smooth_demo.py import numpy as np import cv2 from scipy.ndimage import gaussian_filter1ddef gaussian_smooth_array(arr, sigma):smoothed_arr = gaussian_filter1d(arr, s…

通过ssh连接debian

使用方法 ssh usernameipaddress [inputpasswd]root用户默认无法由ssh连接&#xff0c; 可以通过修改配置 sudo vim /etc/ssh/sshd_config去掉PermitRootLogin前的‘#’,并修改为 PermitRootLogin yes 重启sshd服务 sudo systemctl restart sshd参考 https://linuxconfig.or…

Outlook 无网络连接[2604] 错误解决办法

Outlook 是微软公司开发的一款广泛使用的电子邮件客户端&#xff0c;广泛应用于个人用户和企业办公环境中。然而&#xff0c;许多用户在使用 Outlook 时可能会遇到“无网络连接”或者“错误代码 [2604]”等问题。这个错误通常会导致 Outlook 无法正常连接到邮件服务器&#xff…

.NET 9.0 的 Blazor Web App 项目中 Hash 变换(MD5、Pbkdf2) 使用备忘

一、生成 string 对应的 MD5 码 /// <summary>/// 生成 string 对应的 MD5 码/// </summary>/// <param name"str">需要转换的字符串 string&#xff1a;用于登录认证时&#xff0c;str username 线下传递的key DateTime.Now.Ticks.ToString() …

“UniApp的音频播放——点击视频进入空白+解决视频播放器切换视频时一直加载的问题”——video.js、video-js.css

今天&#xff0c;又解决了一个单子“UniApp的音频播放——点击视频进入空白解决视频播放器切换视频时一直加载的问题” 一、问题描述 在开发一个基于 video.js 的视频播放器时&#xff0c;用户通过上下滑动切换视频时&#xff0c;视频一直处于加载状态&#xff0c;无法正常播放…

P3数据结构、数据类型、数字编码、字符编码:保姆级图文详解

文章目录 前言1、数据结构分类1.1、逻辑结构&#xff1a;线性与非线性1.2、物理结构&#xff1a;连续与分散1.3、数据结构的实现方式1.4、数据结构的选择依据 2、基本数据类型2.1、定义与分类2.2、存储形式 3、数字编码3.1、原码、反码与补码3.2、浮点数编码3.3、整数与浮点数区…