最近作的项目有一个奇葩问题,项目作完后交给甲方,因为甲方数据库比咱们模拟的库在数据量上大好多(单表有几个亿的量)。直接致使接口请求的时间都在30s以上。。。前端
固然数据的优化只能交给后端的同事去作了。可是目前最主要的任务是提供一个能看的前端页面给甲方验收。ios
请求慢的问题暴露了前端的不少缺陷。在补充了加载提示等功能后,用户大量的重复点击产生的重复接口请求致使相同的数据在请求结束后蜂拥而来。数据库
网上找了一些博主的方法(见文未连接),所有都是拦截了前面的请求。好比说发了一个请求a
, 在 a 尚未完成时,又发了一个与 a 同样的请求 a2
,博主们的方法是取消了请求a
只保留a2
axios
以上方法在接口快的时候是不会有问题的。可是接口慢的时候,a
接口都快要请求完成了,这时给它来了个取消。就有点不合理。segmentfault
因此我如今的作法是取消后来的a2
,保留a
。后端
爬一爬axios
官方文档传关门,要取消一个正在发出或已发出可是尚未返回数据的请求,用到的是axios.CancelToken
这个对像里面的方法,以下:数组
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function(thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 处理错误 } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // 取消请求(message 参数是可选的) source.cancel('Operation canceled by the user.');
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // executor 函数接收一个 cancel 函数做为参数 cancel = c; }) }); // cancel the request cancel();
第一种方法是多个请求共用一个token,适合在某一时间同时取消全部请求,第二种方法每一个请求有独立的token,适合每一个请求分状况是否进行单独取消。函数
显然本次需求要用第二种方法。post
核心方法就是在请求时将请求信息存在一个数组里,而后在请求结速以后在这个数组中移除。下次再发请求时就在数组里找是否存在相同的请求,若是存在,那么取消本次请求。测试
参看axios
文档,在请求拦截
与响应拦截
都会反回请求的原始数据。
// 添加请求拦截器 // config中有 url, data, params等信息 axios.interceptors.request.use(function (config) { return config; }); // 添加响应拦截器 // 其中response.config与请求拦截的config是同样的 axios.interceptors.response.use(function (response) { return response; });
const CancelToken = axios.CancelToken let requestQueue = [] // 请求拦截调用 function handleRequest({ config }) { // 提取四个参数用于区分相同的请求 const { url, method, data = {}, params = {} } = config; const jData = JSON.stringify(data),jParams = JSON.stringify(params) const panding = requestQueue.filter(item => { return item.url === url && item.method === method && item.data === jData && item.params === jParams }) if(panding.length){ // 这里是重点,实例化CancelToken时,对参数 c 进行当即调用,便可当即取消当前请求 config.cancelToken = new CancelToken(c => c(`重复的请求被主动拦截: ${url} + ${jData} + ${jParams}`)) }else{ // 若是请求不存在,将数据转为JSON字符串格式,后面比较好对比 requestQueue.push({ url, data: jData, params: jParams, method, }) } } // 响应拦截调用 function handleResponse({ config }) { const { url, data = JSON.stringify({}), params = JSON.stringify({}) } = config let reqQueue = requestQueue.filter(item => { return item.url !== url && item.data !== data && item.params !== params }) requestQueue = reqQueue }
// 请求拦截 axios.interceptors.request.use(function (config) { handleRequest({ config }) return config; }); // 响应拦截器 axios.interceptors.response.use(function (response) { handleResponse({ config: response.config }) return response; });
试运行结果
CancelToken
方法,其实文档没有详细说明清楚何时能够调用这个方法,致使不少开发者觉得只能在panding状太下才可使用,通过个人测试,在请求拦截中就能够调用cancel。Cancel{message: 'url'}
这个信息。找不到缘由。本次更改在写博文时没有通过生产检验,欢迎你们帮我找找bug
参考: