前端接口请求支持内容缓存和过期时间

这篇具有很好参考价值的文章主要介绍了前端接口请求支持内容缓存和过期时间。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前端接口请求支持内容缓存和过期时间

支持用户自定义缓存时间,在规则时间内读取缓存内容,超出时间后重新请求接口

首先封装一下 axios,这一步可做可不做。但是在实际开发场景中都会对 axios 做二次封装,我们在二次封装的 axios 基础上继续封装,增加支持缓存功能

request.js

import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import cache from '@/plugins/cache'
import qs from 'qs'

// 本地开发环境需要加请求头
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
axios.defaults.headers['lang'] = 'CN'
// 创建axios实例
const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL: process.env.VUE_APP_BASE_API,
  // 超时
  timeout: 100000,
})

// request拦截器
service.interceptors.request.use(
  (config) => {
    // 是否需要设置 token
    const isToken = (config.headers || {}).isToken === false
    // 是否需要防止数据重复提交
    const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
    if (getToken() && !isToken) {
      config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
    }
    // get请求映射params参数
    if (config.method === 'get' && config.params) {
      let url = config.url + '?' + qs.stringify(config.params)
      url = url.slice(0, -1)
      config.params = {}
      config.url = url
    }
    if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
      const requestObj = {
        url: config.url,
        data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
        time: new Date().getTime(),
      }
      const sessionObj = cache.session.getJSON('sessionObj')
      if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
        cache.session.setJSON('sessionObj', requestObj)
      } else {
        // 忽略重复请求的地址
        const exUrls = []
        const s_url = sessionObj.url // 请求地址
        const s_data = sessionObj.data // 请求数据
        const s_time = sessionObj.time // 请求时间
        const interval = 3000 // 间隔时间(ms),小于此时间视为重复提交
        if (
          s_data === requestObj.data &&
          requestObj.time - s_time < interval &&
          s_url === requestObj.url &&
          !exUrls.includes(config.url)
        ) {
          const message = '数据正在处理,请勿重复提交'
          return Promise.reject(new Error(message))
        } else {
          cache.session.setJSON('sessionObj', requestObj)
        }
      }
    }
    return config
  },
  (error) => {
    Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  (res) => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || '0'
    // 获取错误信息
    const msg = res.data.message
    // 二进制数据则直接返回
    if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
      return res.data
    }
    if (code === 401 || code === '10006') {
      MessageBox.confirm('登录状态已过期,请重新登录', '系统提示', {
        confirmButtonText: '重新登录',
        cancelButtonText: '取消',
        type: 'warning',
      }).then(() => {
        store.dispatch('LogOut').then(() => {
          location.href = '/login'
        })
      })
    } else if (code !== '0') {
      Message({
        message: msg || '接口请求异常',
        type: 'error',
      })
      return Promise.reject(new Error(msg))
    } else {
      return res.data
    }
  },
  (error) => {
    let { message } = error
    if (message === 'Network Error') {
      message = '后端接口连接异常'
    } else if (message.includes('timeout')) {
      message = '系统接口请求超时'
    } else if (message.includes('Request failed with status code')) {
      message = '系统接口' + message.substr(message.length - 3) + '异常'
    }
    Message({
      message: message,
      type: 'error',
      duration: 5 * 1000,
    })
    return Promise.reject(error)
  }
)

export default service

新建 catchAjax.js ,当我们想用接口缓存时,就用 catchAjax 方法,不想用时还用上面的 request 文件,互不影响

const cacheMap = new Map()
// 定义状态池
const statusMap = new Map()
// 回调callbackMap
const callbackMap = new Map()
// 引入axios
import myAxios from '@/utils/request'
// qs用于序列化对象,将对象序列化为用&拼接的参数
import qs from 'qs'

// 一般只缓存GET接口
function generateCacheKey(request) {
  return request.url + '?' + qs.stringify(request.params)
}

// 返回指定分钟后的时间戳 过期时间
function generateExpTime(minutes) {
  // 获取当前时间戳
  let now = new Date()
  // 添加分钟数
  now.setMinutes(now.getMinutes() + minutes)
  // 返回未来的时间戳
  return now.getTime()
}

// 导出请求方法
export function cacheRequest(request) {
  if (request.method && request.method.toUpperCase() !== 'GET') {
    throw new Error('cacheRequest 仅支持GET请求')
  }
  if (request.expTime && !/^\d+$/.test(request.expTime)) {
    throw new Error('expTime 必须是正整数')
  }
  // 用当前请求的 url + 参数 来当做缓存的key
  const cacheKey = generateCacheKey(request)
  // 判断状态池中是否有数据
  if (statusMap.has(cacheKey)) {
    // 获取当前的状态
    const currentStatus = statusMap.get(cacheKey)
    // 如果接口已经在缓存中,则进入这里
    if (currentStatus === 'complete') {
      // 判断是否过期
      let nowTime = new Date().getTime()
      // 已经过期的数据不能从缓存中取,设置这个状态是pending,重新走接口
      if (nowTime >= cacheMap.get(cacheKey).expTime) {
        statusMap.set(cacheKey, 'pending')
      } else {
        // 没有过期则从缓存中返回数据
        return Promise.resolve(cacheMap.get(cacheKey)?.data)
      }
    }

    if (currentStatus === 'pending') {
      // 判断回调池中是否有数据
      return new Promise((resolve, reject) => {
        if (callbackMap.has(cacheKey)) {
          callbackMap.get(cacheKey).push({
            onSuccess: resolve,
            onError: reject,
          })
        } else {
          callbackMap.set(cacheKey, [
            {
              onSuccess: resolve,
              onError: reject,
            },
          ])
        }
      })
    }
  }
  // 设置接口状态
  statusMap.set(cacheKey, 'pending')

  // 判断是否需要缓存,并且缓存池中有数据时,返回缓存池中的数据
  return myAxios(request)
    .then((res) => {
      // 接口响应成功后吧当前的请求状态设置为complete,下次请求时就会走缓存,不会走网络
      statusMap.set(cacheKey, 'complete')
      // 往缓存中塞数据,同时设置过期时间
      cacheMap.set(cacheKey, {
        data: res,
        // 默认缓存5分钟
        expTime: generateExpTime(request.expTime || 5),
      })
      // 判断在接口响应期间是否有请求,如果有请求,则遍历所有的回调并执行
      if (callbackMap.has(cacheKey)) {
        callbackMap.get(cacheKey).forEach((callback) => {
          callback.onSuccess(res)
        })
        // 响应完数据后吧回调删除
        callbackMap.delete(cacheKey)
      }
      // 返回真实的接口数据
      return res
    })
    .catch((error) => {
      statusMap.delete(cacheKey)
      if (callbackMap.has(cacheKey)) {
        callbackMap.get(cacheKey).forEach((callback) => {
          callback.onError(error)
        })
        callbackMap.delete(cacheKey)
      }
      return Promise.resolve(error)
    })
}

使用方法

<template>
  <div>
    <el-button type="primary" @click="cacheAxios">测试</el-button>
  </div>
</template>

<script>
import { cacheRequest } from '@/utils/catchAjax'

const getArticleList = (params) => {
  return cacheRequest({
    url: 'http://localhost:10086/order/list',
    method: 'get',
    params,
    expTime: 1, // 缓存一分钟
  })
}

export default {
  name: 'index',
  methods: {
    cacheAxios() {
      getArticleList({
        pageNum: 1,
        pageSize: 10,
      }).then((res) => {
        console.log(res)
      })
    },
  },
}
</script>

前端接口请求支持内容缓存和过期时间,vue,前端,缓存

我们在 1 分钟内连续点击按钮,发现只会走一次接口,但是控制台可以打印多次数据文章来源地址https://www.toymoban.com/news/detail-736404.html

到了这里,关于前端接口请求支持内容缓存和过期时间的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包