axios全局封装AbortController取消重复请求

为什么?

问题:为什么axios要配置AbortController?防抖节流不行吗?

分析:

  • 防抖节流本质上是用延时器来操作请求的。
  • 防抖是判断延时器是否存在,如果存在,清除延时器,重新开启一个延时器,只执行最后一次请求。
  • 节流呢,是判断延时器是否存在,如果存在,直接return掉,直到执行完这个延时器。
  • 事实上,这些体验感都不算友好,因为对于用户来说,得等一些时间,尤其是首次请求,不是那么流畅。
  • 而当我们配置了AbortController,就可以中断掉重复的请求,并且首次不需要等待时间,是非常友好的行为。
  • 从axios0.22.0开始,开始支持AbortController,并且废弃CancelToken。之所以我们不用CancelToken来取消请求,是因为CancelToken存在内存泄漏隐患,并不安全,所以官方人员希望用户们升级版本,使用AbortController减少内存泄漏风险。
  • 事实上,凡事用xhr封装的请求库,都可以配置AbortController,如fetch,axios,alova等等
  • 本文封装的原理可以移步至另一篇博客查看:如何避免接口重复请求(axios推荐使用AbortController)-优快云博客

前置条件:

升级axios至最新版本。因为AbortController是从0.22.0开始支持的。如果你的版本已经在0.22.0以上,可以正常使用,不升级亦可。

以笔者升级的项目为例,我这里的版本是很古老的0.18.0,所以我这里是直接把axios升级至目前最新的稳定版本1.7.2。(如果升级失败,可以先移除掉axios,再重新安装。)

//安装axios
npm i axios
//卸载axios
npm uninstall axios

接入步骤:

1.在封装axios的全局文件中,先定义两个变量

在全局封装axios的request.js文件的全局里定义两个变量
isCancel 用来判断请求是否被取消
cacheRequest 用来存储需要取消重复请求的接口
// isCancel-取消标识 可以根据这个值判断请求是否被取消
const { isCancel } = axios
const cacheRequest = {}

2.定义一个函数,用来取消请求和删除cacheRequest里对应的请求

// 删除缓存队列中的请求
function abortCacheRequest(reqKey) {
  if (cacheRequest[reqKey]) {
    // 通过AbortController实例上的abort来终止请求
    cacheRequest[reqKey].abort()
    delete cacheRequest[reqKey]
  }
}

3.在请求拦截器里加入取消重复请求的逻辑

之所以做成根据isAbort标识判断是否使用AbortController,是因为并不是所有的请求都是需要取消重复请求的,就像并不是所有的请求都需要防抖节流一样,所以当我们有需要的时候,再加这个标识就好。

const service = axios.create({
  baseURL: hosts.server, // api 的 base_url
  timeout: 50e3, // request timeout
});

service.interceptors.request.use(config => {
    // isAbort - 是config里配置的是否清除相同请求的标识,不传则默认是不需要清除
    const { url, method, isAbort= false } = config
    if (isAbort) {
        // 请求地址和请求方式组成唯一标识,将这个标识作为取消函数的key,保存到请求队列中
        const reqKey = `${url}&${method}`
        // 如果config传了需要清除重复请求的isAbort,则如果存在重复请求,删除之前的请求
        abortCacheRequest(reqKey)
        // 将请求加入请求队列,通过AbortController来进行手动取消
        const controller = new AbortController()
        config.signal = controller.signal
        cacheRequest[reqKey] = controller
    }
})

4.在响应拦截器里加入判断

如果请求成功后,清除cacheRequest里对应的存储;并且在error里判断,取消的请求不做任何处理

service.interceptors.response.use(res => {
  // 请求成功,从队列中移除
  const { url, method, isAbort = false } = response.config
  if (isAbort) delete cacheRequest[`${url}&${method}`]
}), error => {
  if (isCancel(error)) {
    // 通过AbortController取消的请求不做任何处理
    return Promise.reject({
      message: '重复请求,已取消'
    })
  }
}

5.使用

在调用接口的时候,增加一个isAbort的标识为true就能开启取消重复请求的功能了

//请求示例
export function getList(data) {
  return request({
    url: '/list',
    method: 'post',
    data: data,
    isAbort: true // 配置标识 如果该接口频繁请求 则会中断上次请求保留最新一次请求
  })
}

效果图:

体验感极佳,status为canceled,都是被取消的请求

拓展: 

如何取消xhr的请求:

xhr.abort()

如何取消fetch请求:

let controller = new AbortController();
let signal = controller.signal;

fetch(url, {signal});

controller.abort(); // 取消请求

//监听请求取消情况
signal.addEventListener('abort',
  () => console.log('abort!',signal.aborted)
);

AbortController的作用? 

AbortController 是 Web API 的一部分,主要用于控制一个或多个异步操作的请求。它提供了一种机制来通知这些操作应该提前终止或取消。这个接口特别有用,尤其是在用户可能想要取消正在进行的操作的情况下,比如取消一个长时间运行的网络请求。

### Vue3 中 Axios 封装的最佳实践 #### 创建 Axios 实例并配置默认选项 为了提高代码的可维护性和复用性,在项目中通常会单独创建一个文件来初始化 Axios 实例,并为其设定全局性的配置,比如基础 URL 和超时时间等。 ```javascript // src/utils/request.js import axios from 'axios'; const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // API 基础路径可以从环境变量获取 timeout: 5000 // 请求超时时长 }); ``` 此部分定义了一个名为 `service` 的 Axios 客户端实例[^2]。 #### 添加请求与响应拦截器 通过添加自定义逻辑到请求发出之前以及接收到服务器响应之后的过程当中,可以实现诸如身份验证令牌自动附加、错误统一处理等功能。 ```javascript // Request interceptor service.interceptors.request.use( config => { // Do something before request is sent if (store.getters.token) { // 如果存在 token,则将其加入 header 中 config.headers['Authorization'] = `Bearer ${store.getters.token}`; } return config; }, error => Promise.reject(error) ); // Response interceptor service.interceptors.response.use( response => response.data, error => { Message.error(error.message); // 使用 Element UI 或其他组件库显示提示信息 return Promise.reject(error); } ); ``` 这段代码展示了如何利用拦截器机制增强 HTTP 请求的功能。 #### 构建通用的服务调用方法 为了让业务层能够更加方便地发起网络请求,建议编写一层抽象服务作为桥梁连接底层 Axios 实现和上层应用逻辑。 ```typescript // src/api/index.ts export default class ApiService { static async fetch<T>(config: AxiosRequestConfig): Promise<T> { try { const result = await service(config); return result as T; } catch (err) { throw new Error(`API call failed: ${(err as any).message}`); } } } ``` 这里提供了一种基于 TypeScript 泛型的方式来进行泛化的 API 调用[^3]。 #### 处理取消重复请求的情况 为了避免因页面快速切换或其他原因造成的不必要的并发请求问题,可以通过引入 `AbortController` 来管理请求生命周期。 ```typescript let controller; async function fetchData(url:string){ if(controller){controller.abort();} controller=new AbortController(); try{ let res=await service({...options,signal:controller.signal}) console.log(res); }catch(e){ if(!axios.isCancel(e)){ console.warn('Error:',e.message); } } } ``` 上述例子说明了怎样借助于 `AbortController` 控制器对象的安全终止功能来优化用户体验[^1]。 #### 解决开发环境中遇到的 CORS 问题 当本地开发过程中面临跨源资源共享(CORS)障碍时,可以在项目的构建工具配置里指定代理规则从而绕过浏览器安全策略限制。 ```javascript module.exports = defineConfig({ ... devServer: { proxy: { '/api': { target: 'http://example.com', changeOrigin: true, pathRewrite: {'^/api' : ''} } } } }); ``` 以上设置允许开发者轻松应对不同后端地址带来的兼容性挑战[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零凌林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值