Vue:路由切换终止上个路由未完成请求

说明

在项目测试过程中有一次网络不是很好,测试人员对一个请求多次点击,然后切换路由后,由于上个页面堆积的请求过多,导致该页面请求返回缓慢.之前是没有考虑过要终止,毕竟这也是遇到了,也算是优化.然后我去百度找了各路大佬整理的,发现,请求是终止了,但是新页面的请求也被终止了,于是我就自己研究了一下,对代码进行了自己想法上的整理.(PS:以下代码已经过实践!!如有问题无法实现的可以留言).

代码与讲解

一 思路

1.将所有的请求都收集起来(我这里通过VueX管理这个请求数组).
2.在路由切换的时候通过cenael()进行终止(我用的是axios)

二 代码(以下......省略号为省略代码,与此次实现无关)

1.首先是vuex的代码,
讲解:建立一个请求数组,将请求都装入其中,这里存放在vuex的state中,代码如下:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state:{
    requests:[], // 请求队列
  }
})

export default store

也可对数据处理进行统一的封装

......
mutations: {
    pushRequests(state, source) {
      state.requests = [...state.requests, source];
    },
    emptyRequests(state) {
      if (Setting.cancelPendingReq) {
        state.requests.forEach((xhr) => xhr.cancel());
        state.requests = [];
      }
    }
}

我采用的是modules嵌入vuex,调用示例

// request.js
store.commit('global/api-cancel/pushRequests', source);
// 路由拦截中
store.commit('global/api-cancel/emptyRequests');

2.然后是axios的代码
讲解:我项目是单独封装了request.js所以axios内,代码片段如下:

import axios from 'axios' // 引入axios
import store from '@/store' // 引入store

var CancelToken = axios.CancelToken; // 申明CancelToken 

const service = axios.create({
......
}) // 建立一个axios ,里面参数根据自己需要配置

// 请求拦截
service.interceptors.request.use(
  config => {
    let source = CancelToken.source() // 申明CancelToken,也可new CancelToken.source()实例一个
    config.cancelToken =source.token //讲实例对象的token赋予该请求
    store.state.requests.push(source) // 将该实例添加到队列中
   ......
    return config
  },
  error => {
    return Promise.reject(error)
  }
)
// 响应拦截器(响应拦截在这里其实没什么作用,只不过使用cancel()时候我发现有个不小心会出错的问题)
service.interceptors.response.use(
  response => {
    const res = response.data
    return res
  },
  error => {
// 当cancel()里面没有传参的时候message返回的是undefined,我有专门对error.message进行includes判断这时候我发现报错了,所以我建议大家可以和我一样加一个判断
    if(!error.message){
      return Promise.reject(error)
    }
.......
  }
)
export default service

3.路由守卫的代码
讲解:全局路由前守卫中,每次切换终止请求,并清空队列,代码片段如下:

import router from './router' 
import store from './store'

router.beforeEach(async (to, from, next) => {
  store.state.requests.forEach(xhr=>xhr.cancel()) // 通过遍历终止所有未完成的请求
// 其中xhr=>xhr.cancel()为下面注释代码的简写,
//(xhr) => {
//  return xhr.cancel
//}
  store.state.requests =[] // 执行完清空,等待下一次的页面的请求装载
  next()
})
......

4.其他补充
讲解:当请求被终止的时候,catch中err会被打印,所以加个判断以免一终断就打印一堆,我是这么处理的,代码如下:

.......
catch (err) {
        if(err != 'Cancel'){
          console.log("debug", err);
        }
      }

或者在请求拦截中全局配置请求终止的拦截

......
// 判断是否为请求终止
if (error != 'Cancel') {
        Message.error({
          content: '系统出错了,请稍后重试',
        });
      } else {
        return new Promise(() => {}); // 抛出一个pending的Promise使得结果不进入then也不进入catch,后续需要注意的是如果存在结果then或catch接受请适当挑战
      }

三.总结

1.在整理代码过程中由于对axios的了解不够深入,并不知道为什么要这么做,我的理解是,要给每个请求绑定一个token(source.token),然后将对应的source进行添加队列,也是通过终止source就会终止响应token的请求.
2.以下感谢能看到这篇文章的同学们,希望对你们有帮助.

你可能感兴趣的:(Vue:路由切换终止上个路由未完成请求)