在 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 拦截器中维护请求队列,并在路由全局守卫中统一中断当前页面的请求。
- 封装 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;
- 在路由全局守卫中调用中断方法
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>
全局统一处理方案(适配多组件)
- 封装 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;
- 在路由全局守卫中调用取消方法
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;
关键注意事项
-
CancelToken 是 axios 旧版 API,已被标记为废弃,新项目优先使用 AbortController 。
-
取消请求的错误信息可自定义,通过 isCancel() 方法精准判断是否为主动取消的请求。