Vue中封装带有取消请求的axios

·  阅读 3416

之前使用axios都是调用普通的请求,但是直到项目中遇到了一些稍微复杂的交互,比如输入框搜索需要取消上一次的重复请求,所以结合之前的经验,再封装一次带有取消请求axios方法。(可跳到最后看全部代码)

1. axios的取消事件


方法一:可以使用 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.');
复制代码

方法二: 通过传递一个 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();
复制代码

单独使用axios的时候,两个方法都可以,但是封装好axios方法在vue中全局调用时,方法一有个问题,axios的cancel方法会把即将要发出的请求取消掉,所以用第二个方法

2. 封装取消方法

import axios from 'axios'

// 基本配置
let baseUrl = 'http://127.0.0.1:3000'
let timeout = 60000
Object.assign(axios.defaults, {
  baseURL: baseUrl,
  timeout,
  headers: { 'Content-Type': 'application/json;charset=UTF-8'}
})

// object对象存放每次new CancelToken生成的方法
let source = {}

// 每次请求前都会把api放在此数组中,响应成功后清除此请求api
let requestList = []

// 请求拦截器
axios.interceptors.request.use(config => {
  // do something
  // 查询状态提示...
  // 对请求参数做处理
  return config
}, function (error) {
  return Promise.reject(error)
})

// 响应拦截器
axios.interceptors.response.use(response => {
  // do something
  // 关闭查询状态
  
  // 获取请求的api
  const request = JSON.stringify(response.config.url)
  // 请求完成后,将此请求从请求列表中移除
  requestList.splice(requestList.findIndex(el => el === request), 1)
  return response
}, function (err) {
  // 报错信息没法获取config
  if (axios.isCancel(err)) {
    // 根据业务场景确定是否需要清空
    // 例如:页面跳转前,清空离开页面的请求
    requestList.length = 0
  } else {
    console.log(err)
  }
  return Promise.reject(err)
})

// 定义取消方法
/**
 * 
 * @param {*} api 
 * @param {Boolean} allCancel 
 */
function cancelRequest(api, allCancel){
  // 请求列表里存在此api,即发起重复请求,把之前的请求取消掉
  if (api && requestList.includes(api) && typeof source[api] ==='function'){
    source[api]('终止请求')
  } else if (!api && allCancel) {
    // allCancel为true则请求列表里的请求全部取消
    requestList.forEach(el => {
      source[el]('批量终止请求')
    })
  }
}

function request(method, api, params = {}, options = {}) {
  // 取消上一次请求
  if (requestList.length) cancelRequest(api)
  return new Promise((resolve, reject) => {
    if (method === 'get') {
      options.params = params
    } else {
      options.data = params
    }
    const config = Object.assign(
      {
        url: api,
        method,
        // source对象保存取消方法
        cancelToken: new axios.CancelToken(function executor(c) {
          source[api] = c;
        })
      },
      options
    )
    // 请求前将api推入requestList
    requestList.push(api)
    axios.request(config)
    .then(res => {
      resolve(res)
    })
    .catch(err => {
      console.log(err)
      reject(err)
    })
  })
}

// 把cancelRequest方法暴露出去
const http = {
  // bind偏函数预设参数
  get: request.bind(null, 'get'),
  post: request.bind(null, 'post'),
  cancel: cancelRequest
}
export default http
复制代码
分类:
前端
标签:
分类:
前端
标签: