前端重复点击重复请求的解决方法
在之前的项目中使用原生js写前端经常会遇到重复请求的问题,即一次请求还没有执行完毕,用户又点击了一次,这样重复请求会造成后台数据异常。又比如在查询数据的时候,点击了一次查询,还在处理数据的时候,用户又点击了一次查询。第一次查询执行完毕页面已经有数据展示出来了,用户可能正在看呢,此时第二次查询也处理完返回到前台把页面刷新了,就会造成很不好的体验。 之前的项目中是在请求结束前简单粗暴的直接禁用掉按钮
在之前的项目中使用原生js写前端经常会遇到重复请求的问题,即一次请求还没有执行完毕,用户又点击了一次,这样重复请求会造成后台数据异常。又比如在查询数据的时候,点击了一次查询,还在处理数据的时候,用户又点击了一次查询。第一次查询执行完毕页面已经有数据展示出来了,用户可能正在看呢,此时第二次查询也处理完返回到前台把页面刷新了,就会造成很不好的体验。
之前的项目中是在请求结束前简单粗暴的直接禁用掉按钮,不让用户点击。但是感觉这样不太好,正确的做法应该是才后面的请求发起的时候应该取消掉前面的请求。这时就要使用axios的cancalToken了。
废话有点多,下面开始正题。axios就不多讲了,主要说说axios的拦截器和cancelToken。
axios拦截器
axios的拦截器分为请求拦截器和响应拦截器,顾名思义就是指在请求服务器之前和服务器数据返回到前台的时候可以对数据或者头信息进行操作处理。这里主要是使用请求拦截器,在请求拦截器中取消之前的请求。
请求拦截器
axios.interceptors.request.use(function(config){
// console.log(config);
//例如在请求中拼接头信息
config.headers.token = 'lalal';
return config;
},function(ret){
console.log(ret);
})
响应拦截器
axios.interceptors.response.use(function(config){
// console.log(config);
//例如在请求中拼接头信息
config.headers.token = 'lalal';
return config;
},function(ret){
console.log(ret);
})
cancelToken
这是axios提供的一个api,用法如下
let CancelToken = axios.CancelToken;
var f;
//这里的参数c其实是一个函数
config.cancelToken = new CancelToken(function executor(c){
f = c;
});
//如果需要取消这个请求,调用函数即可
f();
下面结合具体的例子来讲解
页面就一个获得数据的按钮,在我点击的时候会去后台请求数据,我的后台使用soringboot写的,休眠了5s模仿获取数据的过程。
package com.example.demo.controllers;
import com.example.demo.entity.FetchDTO;
import org.springframework.web.bind.annotation.*;
/**
* @program: vue-后端
* @description:
* @create: 2020/07/02 11:22
*/
@RestController
@CrossOrigin(value = "*")
public class FetchController {
@GetMapping(value = "/get/data")
public String getData() throws InterruptedException {
Thread.sleep(5000);
return "222";
}
}
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script src="js/axios.min.js"></script>
<body>
<button onclick="getData()">获得数据</button>
</body>
<script>
//axios拦截器,请求拦截器
let f;//f变量接收取消请求的函数
let CancelToken = axios.CancelToken;//初始化cancelToken对象
axios.interceptors.request.use(function(config){
//在每一步请求开始前执行取消函数
cancelRequest ();
config.cancelToken = new CancelToken(function executor(c){
//每次执行请求之前先将这个请求的取消函数赋给f
f = c;
});
// console.log(f)
// f();
return config;
},function(ret){
console.log(ret);
})
//取消函数,如果取消函数存在则执行这个番薯
function cancelRequest(){
if(f){
f();
}
}
function getData(){
axios.get('/get/data')
.then(result => {
console.log(result.data);
},(error) => {
console.log('222');
})
}
</script>
</html>
逻辑很简单,这里的每个请求都对应着有自己的取消函数,在每次执行之前都将当前请求的取消函数赋给f。然后在发起请求之前判断f存不存在,如果存在则就是前一个请求的取消函数,执行前一个请求的取消函数就可以了。当然这里有不严谨的地方,例如在请求结束后可以重新将f置为空,不然上一个请求已经处理完了也会执行取消。
上面的是最基本的逻辑,可以封装一下针对所有请求都适用
let pending = []; //声明一个数组用于存储每个请求的取消函数和axios标识
let cancelToken = axios.CancelToken;
let removePending = (config) => {
console.log('pending',pending);
for(let p in pending){
if(pending[p].u === config.url.split('?')[0] + '&' + config.method) {
//当当前请求在数组中存在时执行函数体
pending[p].f(); //执行取消操作
pending.splice(p, 1); //数组移除当前请求
}
}
}
//请求拦截
.interceptors.request.use(function(config) {
/*.....*/
removePending(config); //在一个axios发送前执行一下取消操作
config.cancelToken = new cancelToken((c)=>{
// pending存放每一次请求的标识,一般是url + 参数名 + 请求方法,当然你可以自己定义
pending.push({ u: config.url.split('?')[0] +'&' + config.method, f: c});//config.data为请求参数
});
/*.....*/
})
//请求完毕之后将这个请求从pending数组中移除
axios.interceptors.response.use(function(data) {
var identity = data.config.url +'&'+data.config.method;
let index = pending.findIndex(item => item.u == identity);
pending.splice(index,1);
return data;
})
这样写是将所有的请求和他的取消函数放在数组中,每次执行请求前判断当前请求是不是已经在pending那个数组中,如果已经在说明还在执行,则执行上一个请求的取消函数。
更多推荐
所有评论(0)