取消axios请求的官方文档说明:www.axios-js.com/zh-cn/docs/…
单个请求
index.vue--->在请求接口时,除了传递参数,再传递一个this过去。
import requestList from '@/api/list.js'
//发出请求
requestList({id:1},this).then(res=>{}).catch(err=>{})
//取消请求的写法,传进去的是提示
this.cancel("操作取消");
api/list.js---> API接口处,引入axios,定义CancelToken,写new CancelToken
import axios from 'axios'
const CancelToken = axios.CancelToken;
export function searchList(data, that) {
return request({
url: '/list',
method: 'post',
disLoading: true,
data,
cancelToken: new CancelToken(function executor(c) {
that.cancel = c;
})
})
}
全局配置 | Mixin
使用vuex的mixin实现,我跟着代码打了一遍,虽然进入了cancelToken的方法,但是并没有取消请求。 后续:找到没有取消请求的原因了,要注意{ cancelToken: this.cancelToken }这个东西不是请求参数,而是要放到axios config中,与url平级的。另外参考文档里只实现了销毁页面前取消请求,我举一反三——取消上一次请求操作(比如分页点击查询列表,查询第一页再点击第二页,其实是同操作方法,所以取消了第一页的pending请求)。代码如下:
/mixins/cancelTokenMixin.js --->新建一个这个mixin文件,写以下代码,只用于取消请求:
import axios from 'axios';
const CancelToken = axios.CancelToken;
const cancelTokenMixin = {
data() {
return {
cancelToken: null, // cancelToken实例
cancel: null // cancel方法
}
},
created() {
this.newCancelToken();
},
beforeDestroy() {
//离开页面前清空所有请求
this.cancel('取消请求');
},
methods: {
//创建新CancelToken
newCancelToken() {
this.cancelToken = new CancelToken(c => {
this.cancel = c;
});
}
}
}
export default cancelTokenMixin;
具体vue文件--->引入mixin,在请求之前取消上次请求并创建新cancelToken
import cancelTokenMixin from "@/utils/cancelTokenMixin";
import getList from '@/api/list';
mixins: [cancelTokenMixin],
methods:{
handleList(){
//取消上一次请求
this.cancel("操作取消");
//使用新的cancelToken
this.newCancelToken();
//记得增加第二个参数:{ cancelToken: this.cancelToken }
getList({id:1},{ cancelToken: this.cancelToken }).then(res=>{}).catch(err=>{})
}
}
api/list.js---> 接受两个参数,一个是请求参数,还有一个是config,就是上面这个:{ cancelToken: this.cancelToken }
export function getList(data, config) {
return request({
url: '/resource/link/getAllLink',
method: 'post',
disLoading: true,
data,
...config
})
}
全局配置 | interceptors(目前使用方案,嘎嘎好)
上面使用Mixin方法不太灵活,需要在每次请求前手动调用cancelToken方法,多了就不太好用了。。参考了别人的文章,可以在axios 拦截器中实现。主要逻辑就是针对pending请求,把它们保存在set里,发出请求前取消相同的pending请求。但是这种方法有一个严重的缺点就是:它是针对同url且同参数的请求。举个例子来说就明白了——列表的分页,点击第一页时在pending中,这时点击第二页,理应应该取消第一页的pending,如果你用这个路由拦截,由于传参不一致(page=1,page=2),所以无法阻止第一页还在pending。建议这种地方需要特殊处理呢。 先来看看实现方式把--->
utils/cancelRequest.js--->写三个方法。
const qs = require('qs');
const pending = new Map();
import axios from 'axios';
const CancelToken = axios.CancelToken;
/**
* 添加pending请求
*
* **/
export const addPending = (config) => {
const url = [config.method, config.url, qs.stringify(config.params), qs.stringify(config.data)].join('&');
config.cancelToken = config.cancelToken || new CancelToken(c => {
if (!pending.has(url)) {
pending.set(url, c);
}
})
console.log(url);
}
/**
* 取消单个pending请求
* **/
export const cancelPending = (config) => {
const url = [config.method, config.url, qs.stringify(config.params), qs.stringify(config.data)].join('&');
if (pending.has(url)) {
const cancel = pending.get(url);
cancel('cancel request');
pending.delete(url);
}
}
/**
* 清空pending请求(退出页面时)
* **/
export const clearAllPending = () => {
for (const [url, cancel] of pending) {
cancel('cancel request');
}
pending.clear();
}
src/utils/request.js--->拦截请求之前cancelPending和addPending,拦截返回cancelPending
import { cancelPending, addPending } from '@/utils/cancelRequest'
service.interceptors.request.use(
config => {
cancelPending(config) // 在请求开始前,对之前的请求做检查取消操作
addPending(config) // 将当前请求添加到 pending 中
return config
},
error => {
console.log(error) // for debug
return Promise.reject(error)
}
)
service.interceptors.response.use(
response => {
cancelPending(response) // 在请求结束后,移除本次请求
},
error => {
console.log('err', error) // for debug
return Promise.reject(error)
}
)
src/permission.js--->路由守卫中清空pending的请求,这时因为离开页面了就不请求了
import { clearAllPending } from '@/utils/cancelRequest'
router.beforeEach(async (to, from, next) => {
clearAllPending()//清空pending请求
})
遇到的问题: 我在项目里使用了‘全局配置 | interceptors’和局部取消请求的方式,已经成功实践取消请求的功能。但是遇到一个问题,第一次点击查询列表,由于我在el-table组件绑定了v-loading="listLoading",所以设置listLoading为true,以达到加载表格的效果,这时是正常的,请求pengding中,再次点击查询,还是设置了listLoading=true,接着cancel上一次请求并开启新的请求了,可是表格不再有加载效果。。。。 找到原因了,原来是promise请求我写了个catch,在那里有一个listLoading=false。
参考: 单个取消请求:blog.csdn.net/qq_44096635…
vue使用Mixin实现取消请求:github.com/dadaiwei/vu…
axios 拦截器封装取消请求:segmentfault.com/a/119000002…
最近更新
目前interceptors方法用到现在,非常丝滑。但也遇到一个问题——提交form表单也需要支持可取消怎么办? 解决方案:在提交表单的form-data中传中带一个唯一id,然后在cancelPending方法中,唯一url拼接一下,就可以了。 直接拷贝下面这个最新cancelRequest.js
const qs = require("qs");
const pending = new Map();
import axios from "axios";
const CancelToken = axios.CancelToken;
/**
* 添加pending请求
* @param {Object} config - 请求配置
*/
export const addPending = (config) => {
// 生成唯一的请求标识,包括请求方法、请求url、请求参数和请求数据
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data),
].join("&");
// 如果没有设置cancelToken,则新建一个,并将其存入pending
config.cancelToken =
config.cancelToken ||
new CancelToken((c) => {
if (!pending.has(url)) {
pending.set(url, c);
}
});
};
/**
* 取消单个pending请求
* @param {Object} config - 请求配置
*/
export const cancelPending = (config) => {
// 生成唯一的请求标识,包括请求方法、请求url、请求参数和请求数据
let url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data),
].join("&");
// formData类型的接口,都要携带一个唯一id,用来取消单个请求
if (
config.data &&
config.headers &&
config.headers["Content-Type"] === "multipart/form-data"
) {
url += "&" + (config.data.get("uuid") || "");
}
// 如果pending中存在该请求,则取消该请求,并从pending中移除
if (pending.has(url)) {
const cancel = pending.get(url);
cancel("cancel request");
pending.delete(url);
}
};
/**
* 清空所有pending请求(通常在退出页面时调用)
*/
export const clearAllPending = () => {
// 遍历pending,取消所有请求,并清空pending
for (const [url, cancel] of pending) {
cancel("cancel request");
}
pending.clear();
};
/**
* 判断一个字符串是否为JSON格式
* @param {string} str - 需要判断的字符串
* @returns {boolean} - 是否为JSON格式
*/
export function isJSON(str) {
if (typeof str == "string") {
try {
const obj = JSON.parse(str);
if (typeof obj == "object" && obj) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
}