axios详解之实现请求中断

Axiosvue项目中使用最多的一个第三方开源免费的HTTP库,最大的特点在于可以同时兼容浏览器端和NodeJS服务端。底层通过判定不同的运行环境来自动提供不同的适配器,在浏览器端通过原生的XHR对象来发送请求,而在NodeJS服务端则通过内置的http模块来发送请求。

1、基础示例

// 安装 axios
npm install --save axios;

// 导入 axios
import axios from 'axios';
// 创建 axios 实例
const instance = axios.create({
  baseURL: 'https://www.some-domain.com/path/to/example',
  timeout: 30000,
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
  },
});
// 设置 axios 实例默认配置
instance.defaults.headers.common['Authorization'] = '';
instance.defaults.headers.post['Content-Type'] = 'application/json; charset=UTF-8';

// 自定义请求拦截器
instance.interceptors.request.use(config => {
  const token = window.localStorage.getItem('token');
  token && (config.headers['Authorization'] = token);
  return config;
}, error => Promise.reject(error));

// 自定义响应拦截器
instance.interceptors.response.use(response => {
  if (response.status === 200) {
    return Promise.resolve(response.data);
  }
  
  return Promise.reject(response);
}, error => Promise.reject(error));

2、结合Axios提供的CancelToken构造函数来创建一个简单的post请求

const CancelToken = axios.CancelToken;
let cancel;

instance.post('/api/user/123', {
  name: 'new name',
  phone: 'new phone',
}, {
  // CancelToken 构造函数接收一个 executor 函数参数,并且该函数接收一个取消函数 c 用于取消该次请求
  cancelToken: new CancelToken(function executor(c) {
    // 将取消函数赋值到外部变量,方便从外部取消请求
    cancel = c;
  }),
});

// 手动取消请求
cancel();

3、为了避免在每个请求中都需要手动去实例化CancelToken,我们可以巧妙利用request拦截器来整合这部分的逻辑,实现逻辑复用

3.1、首先将缓存逻辑拆分到一个单独的文件中:

// cacheUtils.js
export const CacheUtils = {
  // 存储请求接口地址以及请求体和取消函数之间的映射关系
  cache: {},
  
  // 根据提供的键名 key 取消对应的请求,若未提供则取消全部请求
  clearCache: function (key) {
    if (key) {
      const cancel = this.cache[key];
      if (cancel && typeof cancel === 'function') {
        cancel();
        delete this.cache[key];
      }

      return;
    }

    Object.keys(this.cache).forEach(cacheKey => {
      const cancel = this.cache[cacheKey];
      cancel();
      delete this.cache[cacheKey];
    });
  },
};

3.2、接下来将其应用到请求拦截器和响应拦截器中:

import qs from 'qs';
import { CacheUtils } from './cacheUtils.js';

// 自定义请求拦截器
instance.interceptors.request.use(config => {
  let cacheKey = config.url;
  
  const token = window.localStorage.getItem('token');
  token && (config.headers['Authorization'] = token);
  
  const method = config.method.toLowerCase();
  if (method === 'get' && config.params && typeof config.params === 'object') {
    cacheKey += qs.stringify(config.params, { addQueryPrefix: true });
  }
  
  if (['post', 'put', 'patch'].includes(method) && config.data && typeof config.data === 'object') {
    config.data = qs.stringify(config.data);
    cacheKey += `_${qs.stringify(config.data, { arrayFormat: 'brackets' })}`;
  }
  
  // 每次发送请求之前将上一个未完成的相同请求进行中断
  CacheUtils.cache[cacheKey] && CacheUtils.clearCache(cacheKey);
  
  // 将当前请求所对应的取消函数存入缓存
  config.cancelToken = new axios.CancelToken(function executor(c) {
    CacheUtils.cache[cacheKey] = c;
  });
  
  // 临时保存 cacheKey,用于在响应拦截器中清除缓存
  config.cacheKey = cacheKey;
  
  return config;
}, error => Promise.reject(error));

// 自定义响应拦截器
instance.interceptors.response.use(response => {
  // 响应接收之后清除缓存
  const cacheKey = response.config.cacheKey;
  delete CacheUtils.cache[cacheKey];
  
  if (response.status === 200) {
    return Promise.resolve(response.data);
  }
  
  return Promise.reject(response);
}, error => {
  // 响应异常清除缓存
  if (error.config) {
    const cacheKey = error.config.cacheKey;
    delete CacheUtils.cache[cacheKey];
  }
  
  return Promise.reject(error);
});

3.3、同样提供CacheUtils.clearCache函数来应对需要手动清除未完成请求的应用场景,使用方式:

// 网页卸载前清除缓存
window.addEventListener('beforeunload', () => CacheUtils.clearCache(), false);

// Vue 中路由跳转前清除缓存
router.beforeEach((to, from, next) => { CacheUtils.clearCache(); next(); });

4、总结:在Axios库中,我们借助于其提供的CancelToken构造函数同样实现了请求中断。

你可能感兴趣的:(javascript,vue.js,前端)