美文网首页前端Vue前端开发那些事儿
Vue路由切换&Axios接口取消重复请求

Vue路由切换&Axios接口取消重复请求

作者: 南城FE | 来源:发表于2021-01-13 15:20 被阅读0次

    在日常前端开发中, 经常会遇到频繁发起的重复请求, 会给服务器及网络造成不必要的压力, 可通过取消重复请求解决

    场景

    • 订单数据条件筛选查询
    • 表单提交按钮频繁点击
    • 路由页面切换请求未取消

    解决方案

    在每个请求发起的时候存储当前存储的标记在一个数组或Map中, 针对每个请求的时候在请求拦截中查询是否重复, 如果已重复则取消历史中重复的请求, 再发起当前请求, 如果没有重复, 则添加存储标记并正常请求, 已请求完成的清除存储标记

    axios中如何取消请求

    1. 可以使用 CancelToken.source 工厂方法创建 cancel token,像这样:
    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.');
    
    1. 还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:
    const CancelToken = axios.CancelToken;
    let cancel;
    
    axios.get('/user/12345', {
      cancelToken: new CancelToken(function executor(c) {
        // executor 函数接收一个 cancel 函数作为参数
        cancel = c;
      })
    });
    
    // cancel the request
    cancel();
    

    项目中封装使用

    基本变量定义

    // 是否取消重复请求开关
    const cancelDuplicated = true
    
    // 存储每个请求中的map
    const pendingXHRMap = new Map()
    
    // 取消请求类型定义 便于后期对此类型不做异常处理
    const REQUEST_TYPE = {
      DUPLICATED_REQUEST: 'duplicatedRequest'
    }
    

    设置重复标记的函数

    const duplicatedKeyFn = (config) => {
      // 可在此设置用户自定义其他唯一标识 默认按请求方式 + 请求地址
      return `${config.method}${config.url}`
    }
    

    添加到请求记录

    const addPendingXHR = (config) => {
      if (!cancelDuplicated) {
        return
      }
      const duplicatedKey = JSON.stringify({
        duplicatedKey: duplicatedKeyFn(config),
        type: REQUEST_TYPE.DUPLICATED_REQUEST
      })
      config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
        if (duplicatedKey && !pendingXHRMap.has(duplicatedKey)) {
          pendingXHRMap.set(duplicatedKey, cancel)
        }
      })
    }
    

    删除请求记录

    const removePendingXHR = (config) => {
      if (!cancelDuplicated) {
        return
      }
      const duplicatedKey = JSON.stringify({
        duplicatedKey: duplicatedKeyFn(config),
        type: REQUEST_TYPE.DUPLICATED_REQUEST
      })
      if (duplicatedKey && pendingXHRMap.has(duplicatedKey)) {
        const cancel = pendingXHRMap.get(duplicatedKey)
        cancel(duplicatedKey)
        pendingXHRMap.delete(duplicatedKey)
      }
    }
    

    axios中使用

    // 请求拦截处理
    axios.interceptors.request.use(config => {
        removePendingXHR(config)
        addPendingXHR(config)
        ...
        return config
    })
    
    // 响应拦截处理
    axios.interceptors.response.use(response => {
        removePendingXHR(response.config)
        ...
    }, error => {
        // 如果是取消请求类型则忽略异常处理
        let isDuplicatedType;
        try {
          const errorType = (JSON.parse(error.message) || {}).type
          isDuplicatedType = errorType === REQUEST_TYPE.DUPLICATED_REQUEST;
        } catch (error) {
          isDuplicatedType = false
        }
        if (!isDuplicatedType) {
            // 其他异常处理
        }
    })
    

    Vue中当路由切换页面的时候,将上一个页面的所有请求取消

    router.beforeEach((to, from, next) => {
        // 遍历pendingMap,将上一个页面的所有请求cancel掉
        pendingXHRMap.forEach((cancel) => {
            cancel();
        });
        pendingXHRMap.clear()
    })
    

    参考

    axios官网取消请求

    如何优雅的解决”重复请求“问题

    相关文章

      网友评论

        本文标题:Vue路由切换&Axios接口取消重复请求

        本文链接:https://www.haomeiwen.com/subject/piguaktx.html