Axios+Cache

2023-09-08  本文已影响0人  萘小蒽

使用某种方法缓存特定接口返回的固定数据,避免重复请求。
比如下面的代码,将axios二次封装。

Cache.js

// import { showDialog } from 'vant';

let CACHES = {}
export default class Cache {
  constructor(axios) {
    this.axios = axios
    if (!this.axios) throw new Error('缺少axios实例')
    this.cancelToken = axios.CancelToken
    this.options = {}
  }

  use(options) {
    let defaults = {
      expire: 3600000, // 过期时间 默认一分钟
      storage: false, // 是否开启缓存
      storage_expire: 3600000, // 本地缓存过期时间 默认一小时
      instance: this.axios, // axios的实例对象 默认指向当前axios
      requestConfigFn: null, // 请求拦截的操作函数 参数为请求的config对象 返回一个Promise
      responseConfigFn: null // 响应拦截的操作函数 参数为响应数据的response对象 返回一个Promise
    }
    this.options = Object.assign(defaults, options)
    this.init()
    // if (options && !options.instance) return this.options.instance
  }

  init() {
    let options = this.options
    if (options.storage) {
      // 如果开启本地缓存 则设置一个过期时间 避免时间过久 缓存一直存在
      this._storageExpire('expire').then(() => {
        if (localStorage.length === 0) CACHES = {}
        else mapStorage(localStorage, 'get')
      })
    }
    this.request(options.requestConfigFn)
    this.response(options.responseConfigFn)
  }

  request(cb) {
    let options = this.options
    options.instance.interceptors.request.use(async config => {
      // 判断用户是否返回 config 的 promise
      let newConfig = cb && (await cb(config))
      config = newConfig || config
      if (config.cache) {
        // console.log(config, '缓存');
        let source = this.cancelToken.source()
        config.cancelToken = source.token
        let data = CACHES[config.url]
        let expire = getExpireTime()
        // 判断缓存数据是否存在 存在的话 是否过期 没过期就返回
        if (data && expire - data.expire < this.options.expire) {
          source.cancel(data)
        }
      }
      return config
    })
  }

  response(cb) {
    this.options.instance.interceptors.response.use(
      async response => {
        let newResponse = cb && (await cb(response))
        response = newResponse || response
        if (response.config.cache) {
          let data = {
            expire: getExpireTime(),
            data: response
          }
          CACHES[`${response.config.url}`] = data
          if (response.config.cache) mapStorage(CACHES)
        }
        return response.data
      },
      error => {
        // 返回缓存数据
        // if (error) {
        //   showDialog({
        //   title: '提示',
        //   message: '网络异常,请稍后重试',
        // })
        // }
        if (this.axios.isCancel(error)) {
          return Promise.resolve(error.message.data.data)
        }
        return Promise.reject(error)
      }
    )
  }

  // 本地缓存过期判断
  _storageExpire(cacheKey) {
    return new Promise(resolve => {
      let key = getStorage(cacheKey)
      let date = getExpireTime()
      if (key) {
        // 缓存存在 判断是否过期
        let isExpire = date - key < this.options.storage_expire
        // 如果过期 则重新设定过期时间 并清空缓存
        if (!isExpire) {
          removeStorage()
        }
      } else {
        setStorage(cacheKey, date)
      }
      resolve()
    })
  }
}

/**
 * caches: 缓存列表
 * type: set->存 get->取
 */
function mapStorage(caches, type = 'set') {
  Object.entries(caches).map(([key, cache]) => {
    if (type === 'set') {
      setStorage(key, cache)
    } else {
      // 正则太弱 只能简单判断是否是json字符串
      try {
        CACHES[key] = JSON.parse(cache)
      } catch (error) {
        // console.error(`Invalid JSON format: ${error}`)
        CACHES[key] = cache
      }
    }
  })
}



// 清除本地缓存
function removeStorage() {
  localStorage.clear()
}

// 设置缓存
function setStorage(key, cache) {
  if (typeof cache === 'object') cache = JSON.stringify(cache)
  localStorage.setItem(key, cache)
}

// 获取缓存
function getStorage(key) {
  let data = localStorage.getItem(key)
  return JSON.parse(data)
}

// 设置过期时间
function getExpireTime() {
  return new Date().getTime()
}

requset.js

import axios from 'axios'
import Cache from './Cache'
import { showToast, showConfirmDialog, ToastOptions } from 'vant';
import router from '@/router';
import 'vant/es/dialog/style';
import { stringify } from "qs";
const instance = axios.create({
  // baseURL: '/api',
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json;charset=UTF-8',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild',
    'Access-Control-Allow-Credentials': 'true',
    'X-Requested-With': 'XMLHttpRequest',
  },
  withCredentials: true,
  paramsSerializer: {
    serialize: stringify // or (params) => Qs.stringify(params, {arrayFormat: 'brackets'})
  }
});

let cache = new Cache(axios) // 将当前 axios 对象传入 Cache 中
cache.use({
  expire: 10000,
  storage: true,
  instance, // 如果有自定义axios实例 比如上面的instance 需要将其传入instance 没有则不传
  requestConfigFn: (config: { headers: { Authorization: string | null; }; }) => {
    // 请求拦截自定义操作
    if (config.headers) {
      config.headers.Authorization = localStorage.getItem('token')
    }
    // 需要将config对象通过 Promise 返回 cache 中 也可以使用new Promise的写法
    return Promise.resolve(config)
  },
  responseConfigFn: (res: { data: { code: number; msg: any; localMessage: string | ToastOptions | undefined; }; }) => {
    // 响应拦截的自定义操作
    if (res.data.code) {
      if (res.data.code === 10005) {
        // showToast('登录已过期,请重新登录!')
        localStorage.removeItem('token')
        localStorage.removeItem('userInfo')
        showConfirmDialog({
          title: '提示',
          message: '登录已过期,请重新登录!',
        })
        .then(() => {
          router.push('/login')
        })
        .catch(() => {
          // on cancel
        })

      } else if (res.data.code === 200) {
        return Promise.resolve(res)
      } else if (res.data.code === 10007) {
        localStorage.removeItem('token')
        localStorage.removeItem('userInfo')
        showConfirmDialog({
          title: '提示',
          message: '登录已过期,请重新登录!',
        })
        .then(() => {
          router.push('/login')
        })
        .catch(() => {
          // on cancel
        })
      }else {
        if(res.data.msg){
          showToast(res.data.msg)
        }
        if(res.data.localMessage){
          showToast(res.data.localMessage)
        }
        return Promise.resolve(res)
      }
    }
  }
})

export default instance

api调用

export function getStudios(data){
  return request({
    url: "/home/video/getStudios/v2",
    method: "post",
    cache: true,
    data
  });
}

export function getDailyAttendanceNotice(){
  return request({
    url: "/home/getDailyAttendanceNotice",
    method: "get",
    cache: true,
  });
}
上一篇下一篇

猜你喜欢

热点阅读