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

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

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

首先封装一下 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'

// 本地开发环境需要加请求头
if (process.env.NODE_ENV === 'development') {
  axios.defaults.headers['tenantId'] = 'yikon'
}
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 = [
          '/bioTask/uploadFileByCondition',
          '/LabTq/updateTqTaskRecordById',
          '/cskSamle/uploadVcfFile',
          '/cskReport/updateReport',
        ]

        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>

image-20231030181210907

我们在 1 分钟内连续点击按钮,发现只会走一次接口,但是控制台可以打印多次数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/110201.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何使用 nvm-windows 这个工具来管理你电脑上的Node.js版本

nvm-windows 是一个用于管理在 Windows 上安装的多个 Node.js 版本的工具。以下是安装和使用 nvm-windows 的步骤&#xff1a; 第1步&#xff1a;下载 nvm-windows 访问 nvm-windows 的 GitHub发布页面.下载最新版本的 nvm-setup.zip 文件。 第2步&#xff1a;安装 nvm-wind…

Postman的高级使用,傻瓜式学习【下】

目录 前言 1、全局变量、环境变量 1.1、概念&#xff1a; 1.2、如何设置全局变量、环境变量 1.3、获取全局变量、环境变量 1.4、案例1&#xff1a;手动设置变量&#xff0c;请求参数获取 1.5、案例2&#xff1a;代码设置变量&#xff0c;代码获取变量 2、Postman读取外部…

TypeScript深度剖析:TypeScript 中命名空间与模块的理解?区别?

一、模块 TypeScript 与 ECMAScript 2015 一样&#xff0c;任何包含顶级 import 或者 export 的文件都被当成一个模块 相反地&#xff0c;如果一个文件不带有顶级的import或者export声明&#xff0c;那么它的内容被视为全局可见的 例如我们在在一个 TypeScript 工程下建立一…

canvas:理解canvas / 基础使用 / Demo效果

一、理解Canvas Canvas是一个HTML5元素&#xff0c;用于在Web页面上绘制2D或3D图形。它允许使用JavaScript在网页上创建和操作图形。Canvas的主要功能是绘图&#xff0c;但也可以用来实现其他功能&#xff0c;如动画和交互式游戏。 使用Canvas&#xff0c;可以创建各种形状、…

Vue的快速入门

Vue的快速入门 下载并安装vue.js Vue是一个基于JavaScript实现的框架, 要使用它就需要从[Vue官网]((https://cn.vuejs.org/)下载 vue.js 文件 第一步&#xff1a;打开Vue2官网&#xff0c;点击下图所示的“起步” 第二步&#xff1a;继续点击下图所示的“安装” 第三步&…

深度学习(4)---生成式对抗网络(GAN)

文章目录 一、原理讲述1.1 概念讲解1.2 生成模型和判别模型 二、训练过程2.1 训练原理2.2 损失函数 三、应用 一、原理讲述 1.1 概念讲解 1. 生成式对抗网络&#xff08;Generative Adversarial Network&#xff0c;GAN&#xff09;是一种深度学习模型&#xff0c;是近年来复杂…

万字解析设计模式之原型模式与建造者模式

一、原型模式 1.1概述 原型模式是一种创建型设计模式&#xff0c;其目的是使用已有对象作为原型来创建新的对象。原型模式的核心是克隆&#xff0c;即通过复制已有对象来创建新对象&#xff0c;而不是通过创建新对象的过程中独立地分配和初始化所有需要的资源。这种方式可以节…

nrf52832 开发板入手笔记:J-Flash 蓝牙协议栈烧写

前言 nrf52832 想要开启 蓝牙功能&#xff0c;比如蓝牙主从机功能&#xff0c;需要额外烧写 蓝牙协议栈的固件&#xff1a;softdevice&#xff0c;换句话说&#xff0c;蓝牙协议栈等代码是不开放的&#xff0c;只提供一个 二进制文件。 也就是 nrf52832 Flash 与 RAM 要分区了…

[激光原理与应用-72]:PLC架构与工作原理

目录 一、PLC简介 1.1 概述 1.2 基本组成 1.3 常见的PLC品牌比较 二、PLC程序执行原理 2.1 PLC有操作系统吗&#xff1f; 2.2 PLC程序执行 2.3 PLC编程语言 2.4 PLC编程过程 三、PLC编程工具 3.1 编程工具 四、PLC与工控机协同 4.1 PLC需要配置工控机吗&#xff1…

滴滴学生认证拉新上线了 地推网推百搭项目

滴滴学生认证可以通过“聚量推客”申请&#xff0c;是一手官签服务商 量级足够大 优势&#xff1a;实时数据T1结算 百分百数据 滴滴学生认证可搭配小红书签到拉新、美团私域校园版拉新、美团圈圈拉新一起做&#xff0c;这样能赚取更多佣金

springboot和flask整合nacos,使用openfeign实现服务调用,使用gateway实现网关的搭建(附带jwt续约的实现)

环境准备&#xff1a; 插件版本jdk21springboot 3.0.11 springcloud 2022.0.4 springcloudalibaba 2022.0.0.0 nacos2.2.3&#xff08;稳定版&#xff09;python3.8 nacos部署&#xff08;docker&#xff09; 先创建目录&#xff0c;分别创建config&#xff0c;logs&#xf…

Chatgpt网页版根据关键词自动批量写原创文章软件【可多开自动登录切换gpt账号】

Chatgpt网页版根据关键词自动批量写原创文章软件介绍&#xff1a; 1、需要放入GPT账号和密码放入在账号库.txt里&#xff0c;可以放入多组账号密码&#xff0c;账号切换轮流使用。 2、可以自定义回答指令&#xff0c;也可多个回答指令随机切换。 3、可以给关键词加双标题&…

【EI会议征稿】 2024年遥感、测绘与图像处理国际学术会议(RSMIP2024)

2024年遥感、测绘与图像处理国际学术会议(RSMIP2024) 2024 International Conference on Remote Sensing, Mapping and Image Processing 2024年遥感、测绘与图像处理国际学术会议(RSMIP2024)将于2024年1月19日-21日在中国厦门举行。会议主要围绕遥感、测绘与图像处理等研究领…

宝塔Linux面板Java项目前后端部署 (PHP部署前端文件)

1. 上传前端文件 将整个文件夹拖进来 2. PHP项目 (添加站点) 添加证书SSL 新增配置文件 location /dev-api/{proxy_set_header Host $http_host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header REMOTE-HOST $remote_addr;proxy_set_header X-Forwarded-For $proxy_…

网络新闻发稿为何经久不衰?

有的老板可能看不到新闻营销的直接回报&#xff0c;一直不乐意在此方面投入&#xff0c;但是却看到竞争对手一直在搞新闻营销&#xff0c;也就安排个PR做做新闻公关。小马识途营销顾问观察&#xff0c;自互联网诞生以来&#xff0c;新闻营销一直是网络营销工作中的一个重点。 如…

Mysql系列 -索引数据结构

索引就是排好序的数据结构&#xff0c;可以帮助我们快速的查找到数据&#xff0c;那么底层的数据到底是如何存储的呢&#xff1f; 为什么InnoDB 用的是Btree 存储结构&#xff1f; 大家可以看看这个可视化的网站 数据结构和算法的可视化工具 可以看到数据结构里面有链表&…

学习笔记|配对样本均数T检验|SPSS常用的快捷键|规范表达|《小白爱上SPSS》课程:SPSS第六讲 | 配对样本均数T检验

目录 学习目的软件版本原始文档配对样本均数T检验一、实战案例二、案例解析三、统计策略四、SPSS操作1、正态性检验2、配对样本T检验 五、结果解读六、规范报告1、规范表格2、规范文字 划重点Tips:SPSS常用的快捷键 学习目的 SPSS第六讲 | 配对样本均数T检验 软件版本 IBM S…

【JavaSE专栏58】“Java构造函数:作用、类型、调用顺序和最佳实践“ ⚙️⏱️

解析Java构造函数&#xff1a;作用、类型、调用顺序和最佳实践" &#x1f680;&#x1f4da;&#x1f50d;&#x1f914;&#x1f4dd;&#x1f504;⚙️⏱️&#x1f4d6;&#x1f310; 摘要引言1. 什么是构造函数 &#x1f914;2. 构造函数的类型与用途 &#x1f4dd;1.…

课题学习(九)----阅读《导向钻井工具姿态动态测量的自适应滤波方法》论文笔记

一、 引言 引言直接从原论文复制&#xff0c;大概看一下论文的关键点&#xff1a; 垂直导向钻井工具在近钻头振动和工具旋转的钻井工作状态下&#xff0c;工具姿态参数的动态测量精度不高。为此&#xff0c;通过理论分析和数值仿真&#xff0c;提出了转速补偿的算法以消除工具旋…

【如何写论文】硕博学位论文的结构框架、过程与大纲分析

硕士论文可以说是毕业前最重要的一部分&#xff0c;也可以说是展示和检验你3年研究生学习的成果的一个考试。硕士论文答辩和检验合格&#xff0c;才能够顺利拿到毕业生和学位证&#xff0c;可见其重要性。 目录 一、基础框架1.1、摘要&#xff08;Abstract&#xff09;1.2、绪论…