Vue 缓存Hook:提高接口性能,减少重复请求
前言
在开发 Web 应用时,我们经常会遇到需要重复调用接口的场景。例如,当用户频繁刷新页面或进行某个操作时,我们可能需要多次请求相同的数据。这不仅会增加服务器负担,还会导致用户体验下降。为此,我们可以使用缓存机制来优化这一问题。本文将教你一步一步实现一个功能较完善的Vue缓存Hook(钩子函数),它可以帮助我们减少接口的重复调用,提高应用性能。
介绍
这个Hook是一个基于Vue响应式系统的缓存工具,它可以帮助我们轻松地在组件之间共享和管理缓存数据。通过使用缓存,我们可以将接口调用结果缓存起来,当再次需要相同数据时,可以直接从缓存中获取,避免重复调用接口。
示例
以下是一个简单的示例:
import { reactive } from 'vue';// 缓存值的接口定义
interface CacheValue {data: any; // 存储的数据expireAt: number; // 数据的过期时间戳
}// 使用缓存的功能函数
export function useCache() {// 创建一个响应式的Map对象来存储缓存const cache = reactive<Map<string, CacheValue>>(new Map());/*** @param {string} key - 数据的键* @param {any} data - 要存储的数据* @param {number} cacheTime - 数据的缓存时间(以毫秒为单位)*/function setDataToCache(key: string, data: any, cacheTime: number) {const expireAt = Date.now() + cacheTime; // 计算过期时间戳cache.set(key, { data, expireAt }); // 存储数据和过期时间}/***getDataFromCache函数:尝试从缓存中获取数据*@param {string} key - 数据的键*@returns {any|null} 如果缓存存在且未过期,返回缓存的数据;否则返回null*/function getDataFromCache(key) {const cachedData = cache.get(key);if (cachedData) {const { data, expireAt } = cachedData as CacheValue;if (Date.now() < expireAt) {return data; // 如果未过期,返回数据}cache.delete(key); // 如果已过期,清除缓存项}return null; // 如果不存在或已过期,返回null}// clearExpiredCache函数:清除过期的缓存function clearExpiredCache() {const now = Date.now(); // 获取当前时间cache.forEach((value, key) => {if (value && value.expireAt && value.expireAt < now) {cache.delete(key); // 如果过期,删除缓存项}});}// 设置一个定时器,每60秒执行一次清除过期的缓存setInterval(clearExpiredCache, 60000);// 返回设置数据和获取数据的方法return { setDataToCache, getDataFromCache };
}
这个Hook使用了 Vue 的响应式系统,将一个 Map 对象作为缓存的容器,然后提供了两个方法:setDataToCache 和 getDataFromCache,分别用于设置和获取缓存数据。它还使用了 setInterval 函数来定期清除已过期的缓存项。
我们可以在任何需要缓存数据的组件中,使用这个Hook,例如:
<template><div><h1>用户信息</h1><p v-if="loading">加载中...</p><p v-else-if="error">加载失败</p><p v-else>{{ userInfo }}</p></div>
</template><script setup>import { useCache } from './useCache';import { onMounted, ref } from 'vue';const { setDataToCache, getDataFromCache } = useCache();const userInfo = ref(null);const loading = ref(false);const error = ref(false);async function fetchUserInfo() {loading.value = true;error.value = false;try {// 从缓存中获取用户信息const cachedUserInfo = getDataFromCache('userInfo');if (cachedUserInfo) {// 如果缓存中存在,直接赋值给 userInfouserInfo.value = cachedUserInfo;} else {// 如果缓存中不存在,调用接口获取用户信息const response = await fetch('/api/userInfo');const data = await response.json();// 将用户信息存入缓存中,设置缓存时间为 10 分钟setDataToCache('userInfo', data, 10 * 60 * 1000);// 将用户信息赋值给 userInfouserInfo.value = data;}} catch (err) {error.value = true;} finally {loading.value = false;}}onMounted(fetchUserInfo);
</script>
这样,我们就可以在组件中使用Hook来提高接口的性能,同时保证数据的及时更新。
优化
当然,这个Hook还有很多可以优化和扩展的地方,比如:
错误处理
当前的实现中没有错误处理逻辑。在实际应用中,可能需要添加对异常情况的处理,比如缓存服务不可用时的回退策略。
优化后的代码如下:
import { reactive } from 'vue';// 缓存值的接口定义
interface CacheValue {data: any; // 存储的数据expireAt: number; // 数据的过期时间戳
}// 使用缓存的功能函数
export function useCache() {// 创建一个响应式的Map对象来存储缓存const cache = reactive<Map<string, CacheValue>>(new Map());/*** @param {string} key - 数据的键* @param {any} data - 要存储的数据* @param {number} cacheTime - 数据的缓存时间(以毫秒为单位)*/function setDataToCache(key: string, data: any, cacheTime: number) {try {// 错误处理:确保所有参数都不为空if (!key || !data || !cacheTime) {throw new Error('参数不能为空');}// 错误处理:确保cacheTime是一个有效的正数字if (typeof cacheTime !== 'number' || isNaN(cacheTime) || cacheTime <= 0) {throw new Error('缓存时间必须是一个正数字');}// 计算过期时间戳const expireAt = Date.now() + cacheTime;// 将数据和过期时间存储到缓存中cache.set(key, { data, expireAt });} catch (error) {// 在控制台输出错误信息,方便调试console.error('在设置缓存时发生错误:', error);// 可以根据需要向用户发出警告或执行其他错误处理逻辑}}/***getDataFromCache函数:尝试从缓存中获取数据*@param {string} key - 数据的键*@returns {any|null} 如果缓存存在且未过期,返回缓存的数据;否则返回null*/function getDataFromCache(key) {try {// 如果缓存中存在这个键if (cache.get(key)) {// 获取键对应的缓存对象const { data, expireAt } = cache.get(key) as CacheValue;// 如果当前时间还没有超过过期时间if (Date.now() < expireAt) {// 返回缓存的数据return data;}cache.delete(key); // 清除过期的缓存项}} catch (error) {console.error('在获取缓存数据时发生错误:', error);}// 如果缓存不存在或已过期,返回nullreturn null;}// clearExpiredCache函数:清除过期的缓存function clearExpiredCache() {const now = Date.now(); // 获取当前时间cache.forEach((value, key) => {if (value && value.expireAt && value.expireAt < now) {cache.delete(key); // 如果过期,删除缓存项}});}// 设置一个定时器,每60秒执行一次清除过期的缓存setInterval(clearExpiredCache, 60000);// 返回设置数据和获取数据的方法return { setDataToCache, getDataFromCache };
}
缓存的管理和优化不足
无论缓存是否被使用,都会定期执行清除操作,这可能会造成一些不必要的性能损耗。另外,它也没有考虑到缓存的容量问题,如果缓存中存储了过多的数据,可能会占用过多的内存空间。
考虑这些方面我们的解决方案是使用 setTimeout 函数来为每个缓存项设置一个单独的定时器,当缓存过期时自动清除,避免不必要的性能损耗。同时,可以使用 LRU(最近最少使用)算法来管理缓存的容量,当缓存达到一定的大小时,自动删除最久未使用的缓存项,避免缓存占用过多的空间。优化后的代码如下:
import { reactive } from 'vue';// 缓存值的接口定义
interface CacheValue {data: any; // 存储的数据expireAt: number; // 数据的过期时间戳timer?: any;
}// 使用缓存的功能函数
export function useCache() {// 创建一个响应式的Map对象来存储缓存const cache = reactive<Map<string, CacheValue>>(new Map());// 设置缓存的最大容量const max = 10;// 使用一个数组来存储缓存的键,按照最近使用的顺序排序const keys = [];/*** @param {string} key - 数据的键* @param {any} data - 要存储的数据* @param {number} cacheTime - 数据的缓存时间(以毫秒为单位)*/function setDataToCache(key: string, data: any, cacheTime: number) {try {// 错误处理:确保所有参数都不为空if (!key || !data || !cacheTime) {throw new Error('参数不能为空');}// 错误处理:确保cacheTime是一个有效的正数字if (typeof cacheTime !== 'number' || isNaN(cacheTime) || cacheTime <= 0) {throw new Error('缓存时间必须是一个正数字');}// 计算过期时间戳const expireAt = Date.now() + cacheTime;// 将数据和过期时间存储到缓存中cache.set(key, { data, expireAt });// 为每个缓存项设置一个定时器,当缓存过期时自动清除const timer = setTimeout(() => {cache.delete(key);// 从键数组中移除该键keys.splice(keys.indexOf(key), 1);}, cacheTime);// 将定时器的引用也存储到缓存中,方便取消cache.get(key)!.timer = timer;// 将键添加到键数组的开头keys.unshift(key);// 如果缓存的数量超过了最大容量if (keys.length > max) {// 获取最久未使用的键const lastKey = keys.pop()!;// 清除该键对应的缓存项和定时器clearTimeout(cache.get(lastKey)!.timer);cache.delete(lastKey);}} catch (error) {// 在控制台输出错误信息,方便调试console.error('在设置缓存时发生错误:', error);// 可以根据需要向用户发出警告或执行其他错误处理逻辑}}/***getDataFromCache函数:尝试从缓存中获取数据*@param {string} key - 数据的键*@returns {any|null} 如果缓存存在且未过期,返回缓存的数据;否则返回null*/function getDataFromCache(key) {try {// 如果缓存中存在这个键if (cache.get(key)) {// 获取键对应的缓存对象const { data, expireAt } = cache.get(key) as CacheValue;// 如果当前时间还没有超过过期时间if (Date.now() < expireAt) {// 返回缓存的数据return data;}// 如果缓存已过期,清除缓存项和定时器cache.delete(key);clearTimeout(cache.get(key)!.timer);// 从键数组中移除该键keys.splice(keys.indexOf(key), 1);}} catch (error) {console.error('在获取缓存数据时发生错误:', error);}// 如果缓存不存在或已过期,返回nullreturn null;}// 返回设置数据和获取数据的方法return { setDataToCache, getDataFromCache };
}
清空缓存
除此之外,还缺少一个清空所有缓存的功能:
function clearAllCache() {// 清空缓存中的所有数据cache.clear();// 取消所有的定时器cache.forEach((value) => {clearTimeout(value.timer);});// 清空键数组keys.length = 0;}
最终代码
import { reactive } from 'vue';// 缓存值的接口定义
interface CacheValue {data: any; // 存储的数据expireAt: number; // 数据的过期时间戳timer?: any;
}// 使用缓存的功能函数
export function useCache() {// 创建一个响应式的Map对象来存储缓存const cache = reactive<Map<string, CacheValue>>(new Map());// 设置缓存的最大容量const max = 10;// 使用一个数组来存储缓存的键,按照最近使用的顺序排序const keys = [];/*** @param {string} key - 数据的键* @param {any} data - 要存储的数据* @param {number} cacheTime - 数据的缓存时间(以毫秒为单位)*/function setDataToCache(key: string, data: any, cacheTime: number) {try {// 错误处理:确保所有参数都不为空if (!key || !data || !cacheTime) {throw new Error('参数不能为空');}// 错误处理:确保cacheTime是一个有效的正数字if (typeof cacheTime !== 'number' || isNaN(cacheTime) || cacheTime <= 0) {throw new Error('缓存时间必须是一个正数字');}// 计算过期时间戳const expireAt = Date.now() + cacheTime;// 将数据和过期时间存储到缓存中cache.set(key, { data, expireAt });// 为每个缓存项设置一个定时器,当缓存过期时自动清除const timer = setTimeout(() => {cache.delete(key);// 从键数组中移除该键keys.splice(keys.indexOf(key), 1);}, cacheTime);// 将定时器的引用也存储到缓存中,方便取消cache.get(key)!.timer = timer;// 将键添加到键数组的开头keys.unshift(key);// 如果缓存的数量超过了最大容量if (keys.length > max) {// 获取最久未使用的键const lastKey = keys.pop()!;// 清除该键对应的缓存项和定时器clearTimeout(cache.get(lastKey)!.timer);cache.delete(lastKey);}} catch (error) {// 在控制台输出错误信息,方便调试console.error('在设置缓存时发生错误:', error);// 可以根据需要向用户发出警告或执行其他错误处理逻辑}}/***getDataFromCache函数:尝试从缓存中获取数据*@param {string} key - 数据的键*@returns {any|null} 如果缓存存在且未过期,返回缓存的数据;否则返回null*/function getDataFromCache(key) {try {// 如果缓存中存在这个键if (cache.get(key)) {// 获取键对应的缓存对象const { data, expireAt } = cache.get(key) as CacheValue;// 如果当前时间还没有超过过期时间if (Date.now() < expireAt) {// 返回缓存的数据return data;}// 如果缓存已过期,清除缓存项和定时器cache.delete(key);clearTimeout(cache.get(key)!.timer);// 从键数组中移除该键keys.splice(keys.indexOf(key), 1);}} catch (error) {console.error('在获取缓存数据时发生错误:', error);}// 如果缓存不存在或已过期,返回nullreturn null;}function clearAllCache() {// 清空缓存中的所有数据cache.clear();// 取消所有的定时器cache.forEach((value) => {clearTimeout(value.timer);});// 清空键数组keys.length = 0;}// 返回设置数据和获取数据的方法return { setDataToCache, getDataFromCache, clearAllCache };
}
以上便是一些可优化的点,除此之外,你还可以从持久化、性能监控、并发控制 、缓存策略等方面优化。
结语
在本篇文章中,我们探讨了Vue缓存Hook的使用,这是一个基于Vue响应式系统的缓存工具,旨在帮助我们在组件之间轻松地共享和管理缓存数据。通过使用这个缓存Hook,我们能够有效地提高应用性能,减少不必要的接口调用,从而提升用户体验。希望这篇文章能够帮到大家,特别是在开发Vue应用时需要考虑性能优化和缓存管理的朋友们。如果你有任何问题或疑问,欢迎随时提问,我会尽力帮助解答。
相关文章:
Vue 缓存Hook:提高接口性能,减少重复请求
前言 在开发 Web 应用时,我们经常会遇到需要重复调用接口的场景。例如,当用户频繁刷新页面或进行某个操作时,我们可能需要多次请求相同的数据。这不仅会增加服务器负担,还会导致用户体验下降。为此,我们可以使用缓存机…...
【Python机器学习】用于回归的决策树
用于回归的决策树与用于分类的决策树类似,在DecisionTreeRegressor中实现。DecisionTreeRegressor不能外推,也不能在训练数据范围之外的数据进行预测。 利用计算机内存历史及格的数据进行实验,数据展示: import pandas as pd im…...
numpy库的一些常用函数
文章目录 广播(broadcast)迭代数组数组运算修改数组的形状 修改数组维度连接数组分割数组数组元素的添加与删除Numpy算术函数Numpy 统计函数Numpy排序、条件筛选函数条件筛选 import numpy as np anp.arange(15).reshape(3,5)aarray([[ 0, 1, 2, 3, …...
成员变量与局部变量的区别?
如果你现在需要准备面试,可以关注我的公众号:”Tom聊架构“,回复暗号:”578“,领取一份我整理的50W字面试宝典,可以帮助你提高80%的面试通过率,价值很高!! 语法形式&…...
ES6---判断对象是否为{}
介绍 使用es6语法判断一个对象是否为{} 示例 使用ES6的Object.keys()方法,返回值是对象中属性名组成的数组 let obj {}let keys Object.keys(obj) if(keys.length){alert(对象不为{}) }else{alert(对象为{}) }代码地址 https://gitee.com/u.uu.com/js-test/b…...
高性能、可扩展、分布式对象存储系统MinIO的介绍、部署步骤以及代码示例
详细介绍 MinIO 是一款流行的开源对象存储系统,设计上兼容 Amazon S3 API,主要用于私有云和边缘计算场景。它提供了高性能、高可用性以及易于管理的对象存储服务。以下是 MinIO 的详细介绍及优缺点: 架构与特性: 开源与跨平台&am…...
oracle重启数据库lsnrctl重启监听
oracle重启数据库lsnrctl重启监听 su到oracle用户下,命令 su - oracle切换需要启动的数据库实例: export ORACLE_SIDorcl进入Sqlplus控制台,命令: sqlplus /nolog以系统管理员登录,命令: connect / as sysdba如果是…...
08、Kafka ------ 消息存储相关的配置-->消息过期时间设置、查看主题下的消息存活时间等配置
目录 消息存储相关的配置★ 消息的存储介绍★ 消息过期时间及处理方式演示:log.cleanup.policy 属性配置 ★ 修改指定主题的消息保存时间演示:将 test2 主题下的消息的保存时间设为10个小时1、先查看test2主题下的配置2、然后设置消息的保存时间3、然后再…...
JAVA基础学习笔记-day15-File类与IO流
JAVA基础学习笔记-day15-File类与IO流 1. java.io.File类的使用1.1 概述1.2 构造器1.3 常用方法1、获取文件和目录基本信息2、列出目录的下一级3、File类的重命名功能4、判断功能的方法5、创建、删除功能 2. IO流原理及流的分类2.1 Java IO原理2.2 流的分类2.3 流的API 3. 节点…...
WPF ComboBox限制输入长度
在WPF中,你可以通过两种方式来限制ComboBox的输入长度: 使用PreviewTextInput事件:你可以在这个事件的处理程序中检查输入文本的长度,如果超过最大长度则阻止输入。 <ComboBox PreviewTextInput"ComboBox_PreviewTextIn…...
windows配置网络IP地址的方法
在Windows系统中配置网络IP地址,可以按照以下步骤进行: 打开“控制面板”,选择“网络和Internet”选项。在“网络和Internet”窗口中,单击“网络和共享中心”选项。在“网络和共享中心”窗口中,单击“更改适配器设置”…...
windows配置电脑网络IP的方法
通过控制面板配置IP地址: 打开控制面板: 可以通过在开始菜单中搜索“控制面板”来打开控制面板。选择“网络和Internet”或“网络和共享中心”: 在控制面板中,根据 Windows 版本不同,选中对应的选项进入网络设置。点击…...
MySQL,原子性rename
RENAME TABLE old_table TO backup_table, new_table TO old_table;...
FPGA之按键消抖
目录 1.原理 2.代码 2.1 key_filter.v 2.2 tb_key_filter.v 1.原理 按键分为自锁式按键和机械按键,图左边为自锁式按键 上图为RS触发器硬件消抖,当按键的个数比较多时常常使用软件消抖。硬件消抖会使用额外的器件占用电路板上的空间。 思路就是使用延…...
国内知名的技术平台
1、csdn,中文最大的技术交流平台 2、iteye,程序员的交流平台,归属csdn 3、cnblogs,这个也不错 4、简书也不错...
C#操作注册表
说明 今天用C#开发了一个简单的服务,需要设置成为自启动,网上有很多方法,放到启动运行等,但是今天想介绍一个,通过修改注册表实现,同时介绍一下操作注册表。 private void TestReg(){//仅对当前用户有效 H…...
Unity中BRP下的深度图
文章目录 前言一、在Shader中使用1、在使用深度图前申明2、在片元着色器中 二、在C#脚本中开启摄像机深度图三、最终效果 前言 在之前的文章中,我们实现了URP下的深度图使用。 Unity中URP下使用屏幕坐标采样深度图 在这篇文章中,我们来看一下BRP下深度…...
物联网的感知层、网络层与应用层分享
物联网的概念在很早以前就已经被提出,20世纪末期在美国召开的移动计算和网络国际会议就已经提出了物联网(Internet of Things)这个概念。 最先提出这个概念的是MIT Auto-ID中心的Ashton教授,他在研究RFID技术时,便提出了结合物品编码、互联网…...
kafka KRaft 集群搭建
kafka KRaft集群安装 包下载 https://downloads.apache.org/kafka/3.6.1/kafka_2.13-3.6.1.tgzkafka集群构建好后的数据目录结构 [rootlocalhost data]# tree /data/kafka /data/kafka ├── kafka-1 # 节点1源码目录 ├── kafka-2 # 节点2源码目录 ├── kafka-3 # 节点…...
oracle角色管理
常用角色 CONNECT,RESOURCE,DBA,EXP_FULL_DATABASE,IMP_FULL_DATABASE 1角色可以自定义,语法与创建用户一样 CREATE role role1 IDENTIFIED by 123; 2授权权限给角色 --自定义角色 CREATE role role1 IDENTIFIED by 123; --授权权限给角色 GRANT create view, …...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
