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.rhkb.cn/news/4718.html

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

相关文章

【2025】拥抱未来 砥砺前行

2024是怎样的一年 2024在历史画卷上是波澜壮阔的一年&#xff0c;人工智能的浪潮来临&#xff0c;涌现出无数国产大模型。 22年11月ChatGPT发布&#xff0c;它的出现如同在平静湖面上投下一颗巨石&#xff0c;激起了层层波澜&#xff0c;短短五天用户数就达到了100万&#xff0…

Java设计模式—观察者模式

观察者模式 目录 观察者模式1、什么是观察者模式&#xff1f;2、观察者模式优缺点及注意事项&#xff1f;3、观察者模式实现&#xff1f;4、手写线程安全的观察者模式&#xff1f; 1、什么是观察者模式&#xff1f; - 实例&#xff1a;现实生活中很多事物都是依赖存在的&#x…

鸿蒙开发中的骨架图:提升用户体验的关键一环

大家好&#xff0c;我是小 z&#xff0c;今天要给大家分享一个提升用户体验的超实用技巧 —— 骨架图&#x1f3af; 文章目录 一、什么是骨架图二、骨架图的作用三、鸿蒙开发中实现骨架图的方法1. 利用 opacity 奠定视觉基础2. animateTo 驱动动态变化3. 二者协同触发与展示 四…

vue+高德API搭建前端Echarts图表页面

利用vue搭建Echarts图表页面&#xff0c;在搭建Echarts图表中&#xff0c;如果搭建地理地形图需要准备一些额外的文件&#xff0c;地理json文件和js文件&#xff0c;js文件目前在网上只能找省一级的&#xff0c;json文件有对应的省市县&#xff0c;js文件和json文件对应的也是不…

我在广州学Mysql 系列——触发器的使用

ℹ️大家好&#xff0c;我是练小杰&#xff0c;这周是春节前的最后一周了&#xff0c;现在一双手数都能数得过来了&#xff01;&#xff01; 本播客将学习MYSQL中触发器的相关概念以及基础命令~~ 回顾&#xff1a;&#x1f449;【MYSQL视图相关例题】 数据库专栏&#x1f449;【…

大数据,Hadoop,HDFS的简单介绍

大数据 海量数据&#xff0c;具有高增长率、数据类型多样化、一定时间内无法使用常规软件工具进行捕捉、管理和处理的数据集 合 大数据的特征: 4V Volume : 巨大的数据量 Variety : 数据类型多样化 结构化的数据 : 即具有固定格式和有限长度的数据 半结构化的数据 : 是…

如何用3个月零基础入门网络安全?_网络安全零基础怎么学习

&#x1f91f; 基于入门网络安全/黑客打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 前 言 写这篇教程的初衷是很多朋友都想了解如何入门/转行网络安全&#xff0c;实现自己的“黑客梦”。文章的宗旨是&#xff1a; 1.指出一些自学的误区 2.提供…

微服务与docker

准备工作 在课前资料中给大家提供了黑马商城项目的资料,我们需要先导入这个单体项目。不过需要注意的是,本篇及后续的微服务学习都是基于Centos7系统下的Docker部署,因此你必须做好一些准备: Centos7的环境及一个好用的SSH客户端装好Docker会使用Docker如果是学习过上面Doc…

(7)(7.2) 围栏

文章目录 前言 1 通用设置 2 围栏类型 3 破坏栅栏行动 4 使用 RC 通道辅助开关启用栅栏 5 自动高度规避 6 在任务规划器中启用围栏 7 用于遥控飞行训练 8 MAVLink 支持 前言 ArduPilot 支持基于本机的圆柱形&#xff08;“TinCan”&#xff09;和多边形和/或圆柱形、…

uniapp——App 监听下载文件状态,打开文件(三)

5 实现下载文件并打开 这里演示&#xff0c;导出Excel 表格 文章目录 5 实现下载文件并打开DEMO监听下载进度效果图为什么 totalSize 一直为0&#xff1f; 相关Api&#xff1a; downloader DEMO 提示&#xff1a; 请求方式支持&#xff1a;GET、POST&#xff1b;POST 方式需要…

mybatis的多对一、一对多的用法

目录 1、使用VO聚合对象&#xff08;可以解决这两种情况&#xff09; 多对一&#xff1a; 一对多&#xff1a; 2、非聚合的多对一做法&#xff1a; 3、非聚合的一对多做法&#xff1a; 1、使用VO聚合对象&#xff08;可以解决这两种情况&#xff09; 当我需要多对一、一对…

Vscode:问题解决办法 及 Tips 总结

Visual Studio Code&#xff08;简称VSCode&#xff09;是一个功能强大的开源代码编辑器&#xff0c;广泛用于各种编程语言和开发场景&#xff0c;本博客主要记录在使用 VSCode 进行verilog开发时遇到的问题及解决办法&#xff0c;使用过程中的技巧 文章目录 扩展安装失败调试配…

MySQL 窗口函数

MySQL 窗口函数 1&#xff0c;窗口函数 1.1&#xff0c;什么是窗口函数1.2&#xff0c;基本语法 2&#xff0c;函数详解 2.1&#xff0c;聚合函数2.2&#xff0c;排序函数2.3&#xff0c;偏移函数2.4&#xff0c;值函数 3&#xff0c;进阶用法 1&#xff0c;窗口函数 1.1&am…

基于vite+vue3+mapbox-gl从零搭建一个项目

下面是基于 Vite、Vue 3 和 Mapbox GL 从零搭建一个项目的完整步骤&#xff0c;包括环境搭建、依赖安装、配置和代码示例。 文章目录 1. 初始化项目2. 安装 mapbox-gl 依赖3. 配置 Mapbox Access Token4. 实现地图组件5. 在 App.vue 中使用地图组件6. 启动开发服务器7. 添加自定…

Data Filtering Network 论文阅读和理解

目录 一、TL&#xff1b;DR 二、Introduction 2.1 apple的结论 2.2 业界做法&#xff1a; 2.3 我们的做法&#xff08;Apple&#xff09; 2.4 如何获取好的DFN 三、未完待续&#xff08;这周出去购物了&#xff0c;下周继续补充&#xff09; 一、TL&#xff1b;DR 核心…

ingress-nginx代理tcp使其能外部访问mysql

一、helm部署mysql主从复制 helm repo add bitnami https://charts.bitnami.com/bitnami helm repo updatehelm pull bitnami/mysql 解压后编辑values.yaml文件&#xff0c;修改如下&#xff08;storageclass已设置默认类&#xff09; 117 ## param architecture MySQL archit…

浅谈安科瑞电能质量监测和治理产品在分布式光伏电站的应用-安科瑞 蒋静

1 概述 随着对可再生能源需求的增加&#xff0c;分布式光伏电站的建设和发展迅速。然而&#xff0c;分布式光伏电站的运行过程中面临着一系列问题&#xff0c;比如导致企业关口计量点功率因数过低、谐波污染等。这些问题不仅影响光伏电站自身的运行效率&#xff0c;还会对企业…

CSS实现实现票据效果 mask与切图方式

一、“切图”的局限性 传统的“切图”简单暴力,但往往缺少适应性。 适应性一般有两种,一是尺寸自适应,二是颜色可以自定义。 举个例子,有这样一个优惠券样式 关于这类样式实现技巧,之前在这篇文章中有详细介绍: CSS 实现优惠券的技巧 不过这里略微不一样的地方是,两个…

ToDesk云电脑、顺网云、网易云、易腾云、极云普惠云横测对比:探寻电竞最佳拍档

一、云电脑&#xff1a;电竞新宠崛起 在电竞游戏不断发展的今天&#xff0c;硬件性能成为了决定游戏体验的关键因素。为了追求极致的游戏画面与流畅度&#xff0c;玩家们往往需要投入大量资金购置高性能电脑。然而&#xff0c;云电脑技术的出现&#xff0c;为玩家们提供了一种…

Kotlin Bytedeco OpenCV 图像图像50 仿射变换 图像缩放

Kotlin Bytedeco OpenCV 图像图像50 仿射变换 图像缩放 1 添加依赖2 测试代码3 测试结果 在OpenCV中&#xff0c;仿射变换&#xff08;Affine Transformation&#xff09;和透视变换&#xff08;Perspective Transformation&#xff09;是两种常用的图像几何变换方法。 变换方…