如何避免多次调用同一接口

在 Vue 2 中,想要在离开路由前终止未完成的接口请求,核心是结合 vue-router 的导航守卫 beforeRouteLeave ,搭配 AbortController 或 axios CancelToken 来中断请求。以下是两种可直接复用的方案:

方案1:基于 AbortController (推荐,axios 0.22.0+ 支持)

适用于大多数现代项目,在组件内维护请求控制器实例,路由离开时主动中断。

javascript

<template>

<div>数据加载中...</div>

</template>

<script>

export default {

data() {

return {

abortController: null, // 存储请求控制器

listData: []

};

},

methods: {

async fetchList() {

// 若已有未完成请求,先中断

if (this.abortController) {

this.abortController.abort();

}

// 创建新的控制器

this.abortController = new AbortController();

try {

const res = await this.$axios.get('/api/getList', {

signal: this.abortController.signal // 关联中断信号

});

this.listData = res.data;

} catch (err) {

// 过滤主动中断的错误,避免控制台报错

if (err.name !== 'AbortError') {

console.error('请求失败', err);

}

}

}

},

// 路由离开前的守卫

beforeRouteLeave(to, from, next) {

// 中断未完成的请求

if (this.abortController) {

this.abortController.abort();

this.abortController = null; // 重置

}

next(); // 必须调用,否则无法跳转路由

},

mounted() {

this.fetchList(); // 组件挂载时发起请求

}

};

</script>

方案2:全局请求管理 + 路由守卫(适合多组件场景)

如果项目中有多个组件需要处理,可在 axios 拦截器中维护请求队列,并在路由全局守卫中统一中断当前页面的请求。

  1. 封装 axios 请求,记录请求标识

javascript

// utils/request.js

import axios from 'axios';

const service = axios.create({ baseURL: '/api' });

// 存储当前页面的请求控制器:{ controller, url }

let pendingRequests = [];

// 添加请求到队列

const addPending = (config, controller) => {

pendingRequests.push({ controller, url: config.url });

};

// 中断当前页面所有未完成请求

export const cancelAllPending = () => {

pendingRequests.forEach(item => item.controller.abort());

pendingRequests = []; // 清空队列

};

// 请求拦截器

service.interceptors.request.use(config => {

const controller = new AbortController();

config.signal = controller.signal;

addPending(config, controller);

return config;

});

// 响应拦截器

service.interceptors.response.use(

res => {

// 请求完成后移除队列

pendingRequests = pendingRequests.filter(item => item.url !== res.config.url);

return res;

},

err => {

if (err.name !== 'AbortError') {

pendingRequests = pendingRequests.filter(item => item.url !== err.config?.url);

}

return Promise.reject(err);

}

);

export default service;

  1. 在路由全局守卫中调用中断方法

javascript

// router/index.js

import Vue from 'vue';

import Router from 'vue-router';

import { cancelAllPending } from '@/utils/request';

import Home from '@/views/Home';

Vue.use(Router);

const router = new Router({

routes: [{ path: '/', name: 'Home', component: Home }]

});

// 全局路由离开守卫

router.beforeEach((to, from, next) => {

// 离开当前路由前,中断所有未完成请求

cancelAllPending();

next();

});

export default router;

关键注意事项:

以下是 axios 低版本(<0.22.0) 基于 CancelToken 在组件内实现路由离开前中断未完成请求的完整代码,适配 Vue 2 组件内守卫:

组件内单独处理方案

javascript

<template>

<div>数据加载中...</div>

</template>

<script>

import axios from 'axios';

// 引入 CancelToken 和 isCancel 方法

const { CancelToken, isCancel } = axios;

export default {

data() {

return {

cancelRequest: null, // 存储取消请求的方法

listData: []

};

},

methods: {

async fetchList() {

// 若存在未完成请求,先取消

if (this.cancelRequest) {

this.cancelRequest('路由跳转,中断请求');

this.cancelRequest = null; // 重置取消方法

}

try {

const res = await axios.get('/api/getList', {

// 创建 CancelToken 实例

cancelToken: new CancelToken(c => {

// c 就是取消请求的方法,赋值给变量

this.cancelRequest = c;

})

});

this.listData = res.data;

} catch (err) {

// 过滤主动取消的请求错误,避免控制台报错

if (!isCancel(err)) {

console.error('请求失败:', err);

}

}

}

},

// 组件内路由离开守卫

beforeRouteLeave(to, from, next) {

// 离开路由时,取消未完成的请求

if (this.cancelRequest) {

this.cancelRequest('路由跳转,中断请求');

this.cancelRequest = null;

}

next(); // 必须调用,否则无法跳转

},

mounted() {

this.fetchList(); // 组件挂载时发起请求

}

};

</script>

全局统一处理方案(适配多组件)

  1. 封装 axios 并维护取消请求队列

javascript

// utils/request.js

import axios from 'axios';

const { CancelToken, isCancel } = axios;

const service = axios.create({ baseURL: '/api' });

// 存储当前页面的取消请求方法集合

let cancelRequestArr = [];

// 添加取消方法到队列

const addCancelRequest = (cancel) => {

cancelRequestArr.push(cancel);

};

// 取消所有未完成请求

export const cancelAllRequest = () => {

cancelRequestArr.forEach(cancel => {

cancel('路由跳转,全局中断请求');

});

cancelRequestArr = []; // 清空队列

};

// 请求拦截器

service.interceptors.request.use(config => {

config.cancelToken = new CancelToken(c => {

addCancelRequest(c);

});

return config;

});

// 响应拦截器

service.interceptors.response.use(

res => res,

err => {

// 过滤主动取消的错误

if (!isCancel(err)) {

return Promise.reject(err);

}

}

);

export default service;

  1. 在路由全局守卫中调用取消方法

javascript

// router/index.js

import Vue from 'vue';

import Router from 'vue-router';

import { cancelAllRequest } from '@/utils/request';

import Home from '@/views/Home';

Vue.use(Router);

const router = new Router({

routes: [{ path: '/', name: 'Home', component: Home }]

});

// 全局路由守卫:跳转前取消所有请求

router.beforeEach((to, from, next) => {

cancelAllRequest();

next();

});

export default router;

关键注意事项

  1. CancelToken 是 axios 旧版 API,已被标记为废弃,新项目优先使用 AbortController 。

  2. 取消请求的错误信息可自定义,通过 isCancel() 方法精准判断是否为主动取消的请求。

相关推荐
程序员清洒6 分钟前
Flutter for OpenHarmony:Text — 文本显示与样式控制
开发语言·javascript·flutter
雨季66640 分钟前
Flutter 三端应用实战:OpenHarmony 简易“动态内边距调节器”交互模式深度解析
javascript·flutter·ui·交互·dart
天人合一peng1 小时前
Unity中button 和toggle监听事件函数有无参数
前端·unity·游戏引擎
会飞的战斗鸡1 小时前
JS中的链表(含leetcode例题)
javascript·leetcode·链表
方也_arkling2 小时前
别名路径联想提示。@/统一文件路径的配置
前端·javascript
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 基于web教师继续教育系统的设计与实现为例,包含答辩的问题和答案
前端
qq_177767372 小时前
React Native鸿蒙跨平台剧集管理应用实现,包含主应用组件、剧集列表、分类筛选、搜索排序等功能模块
javascript·react native·react.js·交互·harmonyos
qq_177767372 小时前
React Native鸿蒙跨平台自定义复选框组件,通过样式数组实现选中/未选中状态的样式切换,使用链式调用替代样式数组,实现状态驱动的样式变化
javascript·react native·react.js·架构·ecmascript·harmonyos·媒体
web打印社区2 小时前
web-print-pdf:突破浏览器限制,实现专业级Web静默打印
前端·javascript·vue.js·electron·html
RFCEO2 小时前
前端编程 课程十三、:CSS核心基础1:CSS选择器
前端·css·css基础选择器详细教程·css类选择器使用方法·css类选择器命名规范·css后代选择器·精准选中嵌套元素