网络请求处理axios与本地缓存管理

1,097 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

本文写于2018年7月,笔者当时正在为更换框架做准备,之前使用的是jQuery的.ajax,切换为vue后计划改用官方推荐的axios作为网络请求库,这里根据当时的业务需要,对其配置与特性做一些整理,在请求上增加了本地缓存处理,加快访问速度与减轻服务端压力。在初步探索版使用的过程中需要处理网络请求从服务端拉取数据,用到了axios来替代以往使用的.ajax,切换为vue后计划改用官方推荐的axios作为网络请求库,这里根据当时的业务需要,对其配置与特性做一些整理,在请求上增加了本地缓存处理,加快访问速度与减轻服务端压力。 在初步探索版使用的过程中需要处理网络请求从服务端拉取数据,用到了axios来替代以往使用的.ajax,记一下遇到的问题及解决方案。

相关文档 官方文档   拦截+路由跳转demo

Q:用户登录信息,每个请求都需要带上的参数,使用繁琐还容易出错。所有请求路径预处理

A:使用默认配置 ,每次请求都会带上此数据

axios.defaults.data = {
  uid: localStorage.uid,
};

设置baseURL

axios.defaults.baseURL = '/api';

Q:get方式和post方式传参使用不同的参数,get使用params,post使用data,使用麻烦

A:使用拦截器,在请求发出前拦截,统一处理参数,如果是get方式,则将data赋值给params

Q:post方式默认是json,目前后台识别的格式是application/x-www-form-urlencoded

A:使用官方推荐的方式,用qs.stringify处理data数据再发送,在请求拦截器里进行

axios.interceptors.request.use((config) => {
  // Do something before request is sent
 if (config && config.data) {
    config.data = qs.stringify(config.data);
 }
  return config;
}
Q:在业务开发中,部分请求的数据一般不会变化,但使用次数缺比较频繁,希望将一部分指定请求缓存到本地,下次如果遇到相同参数的请求调用,直接返回本地缓存数据,不再从服务器上获取

A:对axios做了一层封装,将需要缓存的请求的url+参数处理成一个字符串作为key,存入sessionStorage里,当再次遇到该请求时,优先从sessionStorage获取数据返回,如果缓存中没有,则发起网络请求从服务端拉取数据

最后形成整体配置

import Vue from 'vue';
import axios from 'axios';
import qs from 'qs';
import bzt from '@/utils/bzt';
axios.defaults.baseURL = '';
if (process.env.NODE_ENV !== 'production') {
  axios.defaults.baseURL = '/api';
}

const config = {
};
const urlParam = bzt.getParam();
/**
 * axios的封装,带有本地缓存功能的axios,同时要求参数必须放在data里,规范url
 * @param config 配置项
 * @return {Promise<unknown>|*}
 */
const likeAxios = (config) => {
  if (config.url && config.url.includes('?')) {
    throw SyntaxError('url必须存在且不能包含?字符');
  }

  let cacheData = null;
  config.data = config.data || {};
  if (!config.withoutOtherParam) {
    config.data.isAjax = true;
  }
  config.urlJson = `${config.url}?${qs.stringify(config.data)}`;
  if (config.cache && window.sessionStorage) {
    cacheData = window.sessionStorage.getItem(config.urlJson);
    config.data.__cache = {
      urlJson: config.urlJson,
    }
  }
  if (cacheData) {
    return getCacheData(config, cacheData);
  } else {
    return _axios(config);
  }
};
/**
 * 获取缓存数据
 * @param config 配置信息
 * @param cacheData 缓存数据库
 * @return {Promise<unknown>}
 */
const getCacheData = (config, cacheData) => {
  return new Promise ((resolve, reject) => {
    let data = null;
    try {
      data = JSON.parse(cacheData);
    }
    catch (e) {
      window.sessionStorage.removeItem(config.urlJson)
    }
    if (data) {
      resolve(data);
    } else {
      _axios(config)
        .then((result) => {
          resolve(result);
        })
        .catch((e) =>{
          reject(e);
        });
    }
  });
};

const _axios = axios.create(config);
_axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
_axios.interceptors.request.use(
  (config) => {
  // Do something before request is sent
    if (config) {
      config.data = config.data || {};
      if (config.data.__cache) {
        config.__cache = config.data.__cache;
        delete config.data.__cache;
      }
      if (config.method === 'get') {
        config.params = config.data;
      } else if (!config.formData) {
        config.data = qs.stringify(config.data);
      }

    }
    return config;
  }
  , error =>
  // Do something with request error
    Promise.reject(error),
);
_axios.interceptors.response.use((response) => {
  // Do something with response data
  if (response && response.data) {
  // code === 4 代表未登录
      if (!response.config.noRedirect && window.location.hostname !== 'localhost' && !response.data.success && response.data.code !== undefined && response.data.code === 4) {
        if (window.sessionStorage) {
          window.sessionStorage.setItem('goto', window.location.href);
        }
        window.location = '/web_pc/login.html';
      }
    return response.data;
  }
  return response;
}, error =>
// Do something with response error
  Promise.reject(error));

Plugin.install = function (Vue, options) {
  Vue.axios = likeAxios;
  window.axios = likeAxios;
  Object.defineProperties(Vue.prototype, {
    axios: {
      get() {
        return likeAxios;
      },
    },
    $axios: {
      get() {
        return likeAxios;
      },
    },
  });
};

Vue.use(Plugin);

export default Plugin;