当前位置:   article > 正文

Vue 缓存Hook:提高接口性能,减少重复请求

Vue 缓存Hook:提高接口性能,减少重复请求

 前言

在开发 Web 应用时,我们经常会遇到需要重复调用接口的场景。例如,当用户频繁刷新页面或进行某个操作时,我们可能需要多次请求相同的数据。这不仅会增加服务器负担,还会导致用户体验下降。为此,我们可以使用缓存机制来优化这一问题。本文将教你一步一步实现一个功能较完善的Vue缓存Hook(钩子函数),它可以帮助我们减少接口的重复调用,提高应用性能。

介绍

这个Hook是一个基于Vue响应式系统的缓存工具,它可以帮助我们轻松地在组件之间共享和管理缓存数据。通过使用缓存,我们可以将接口调用结果缓存起来,当再次需要相同数据时,可以直接从缓存中获取,避免重复调用接口。

示例

以下是一个简单的示例:

  1. import { reactive } from 'vue';
  2. // 缓存值的接口定义
  3. interface CacheValue {
  4. data: any; // 存储的数据
  5. expireAt: number; // 数据的过期时间戳
  6. }
  7. // 使用缓存的功能函数
  8. export function useCache() {
  9. // 创建一个响应式的Map对象来存储缓存
  10. const cache = reactive<Map<string, CacheValue>>(new Map());
  11. /**
  12. * @param {string} key - 数据的键
  13. * @param {any} data - 要存储的数据
  14. * @param {number} cacheTime - 数据的缓存时间(以毫秒为单位)
  15. */
  16. function setDataToCache(key: string, data: any, cacheTime: number) {
  17. const expireAt = Date.now() + cacheTime; // 计算过期时间戳
  18. cache.set(key, { data, expireAt }); // 存储数据和过期时间
  19. }
  20. /**
  21. *getDataFromCache函数:尝试从缓存中获取数据
  22. *@param {string} key - 数据的键
  23. *@returns {any|null} 如果缓存存在且未过期,返回缓存的数据;否则返回null
  24. */
  25. function getDataFromCache(key) {
  26. const cachedData = cache.get(key);
  27. if (cachedData) {
  28. const { data, expireAt } = cachedData as CacheValue;
  29. if (Date.now() < expireAt) {
  30. return data; // 如果未过期,返回数据
  31. }
  32. cache.delete(key); // 如果已过期,清除缓存项
  33. }
  34. return null; // 如果不存在或已过期,返回null
  35. }
  36. // clearExpiredCache函数:清除过期的缓存
  37. function clearExpiredCache() {
  38. const now = Date.now(); // 获取当前时间
  39. cache.forEach((value, key) => {
  40. if (value && value.expireAt && value.expireAt < now) {
  41. cache.delete(key); // 如果过期,删除缓存项
  42. }
  43. });
  44. }
  45. // 设置一个定时器,每60秒执行一次清除过期的缓存
  46. setInterval(clearExpiredCache, 60000);
  47. // 返回设置数据和获取数据的方法
  48. return { setDataToCache, getDataFromCache };
  49. }

这个Hook使用了 Vue 的响应式系统,将一个 Map 对象作为缓存的容器,然后提供了两个方法:setDataToCache 和 getDataFromCache,分别用于设置和获取缓存数据。它还使用了 setInterval 函数来定期清除已过期的缓存项。

我们可以在任何需要缓存数据的组件中,使用这个Hook,例如:

  1. <template>
  2. <div>
  3. <h1>用户信息</h1>
  4. <p v-if="loading">加载中...</p>
  5. <p v-else-if="error">加载失败</p>
  6. <p v-else>{{ userInfo }}</p>
  7. </div>
  8. </template>
  9. <script setup>
  10. import { useCache } from './useCache';
  11. import { onMounted, ref } from 'vue';
  12. const { setDataToCache, getDataFromCache } = useCache();
  13. const userInfo = ref(null);
  14. const loading = ref(false);
  15. const error = ref(false);
  16. async function fetchUserInfo() {
  17. loading.value = true;
  18. error.value = false;
  19. try {
  20. // 从缓存中获取用户信息
  21. const cachedUserInfo = getDataFromCache('userInfo');
  22. if (cachedUserInfo) {
  23. // 如果缓存中存在,直接赋值给 userInfo
  24. userInfo.value = cachedUserInfo;
  25. } else {
  26. // 如果缓存中不存在,调用接口获取用户信息
  27. const response = await fetch('/api/userInfo');
  28. const data = await response.json();
  29. // 将用户信息存入缓存中,设置缓存时间为 10 分钟
  30. setDataToCache('userInfo', data, 10 * 60 * 1000);
  31. // 将用户信息赋值给 userInfo
  32. userInfo.value = data;
  33. }
  34. } catch (err) {
  35. error.value = true;
  36. } finally {
  37. loading.value = false;
  38. }
  39. }
  40. onMounted(fetchUserInfo);
  41. </script>

这样,我们就可以在组件中使用Hook来提高接口的性能,同时保证数据的及时更新。

优化

当然,这个Hook还有很多可以优化和扩展的地方,比如:

错误处理

当前的实现中没有错误处理逻辑。在实际应用中,可能需要添加对异常情况的处理,比如缓存服务不可用时的回退策略。

优化后的代码如下:

  1. import { reactive } from 'vue';
  2. // 缓存值的接口定义
  3. interface CacheValue {
  4. data: any; // 存储的数据
  5. expireAt: number; // 数据的过期时间戳
  6. }
  7. // 使用缓存的功能函数
  8. export function useCache() {
  9. // 创建一个响应式的Map对象来存储缓存
  10. const cache = reactive<Map<string, CacheValue>>(new Map());
  11. /**
  12. * @param {string} key - 数据的键
  13. * @param {any} data - 要存储的数据
  14. * @param {number} cacheTime - 数据的缓存时间(以毫秒为单位)
  15. */
  16. function setDataToCache(key: string, data: any, cacheTime: number) {
  17. try {
  18. // 错误处理:确保所有参数都不为空
  19. if (!key || !data || !cacheTime) {
  20. throw new Error('参数不能为空');
  21. }
  22. // 错误处理:确保cacheTime是一个有效的正数字
  23. if (typeof cacheTime !== 'number' || isNaN(cacheTime) || cacheTime <= 0) {
  24. throw new Error('缓存时间必须是一个正数字');
  25. }
  26. // 计算过期时间戳
  27. const expireAt = Date.now() + cacheTime;
  28. // 将数据和过期时间存储到缓存中
  29. cache.set(key, { data, expireAt });
  30. } catch (error) {
  31. // 在控制台输出错误信息,方便调试
  32. console.error('在设置缓存时发生错误:', error);
  33. // 可以根据需要向用户发出警告或执行其他错误处理逻辑
  34. }
  35. }
  36. /**
  37. *getDataFromCache函数:尝试从缓存中获取数据
  38. *@param {string} key - 数据的键
  39. *@returns {any|null} 如果缓存存在且未过期,返回缓存的数据;否则返回null
  40. */
  41. function getDataFromCache(key) {
  42. try {
  43. // 如果缓存中存在这个键
  44. if (cache.get(key)) {
  45. // 获取键对应的缓存对象
  46. const { data, expireAt } = cache.get(key) as CacheValue;
  47. // 如果当前时间还没有超过过期时间
  48. if (Date.now() < expireAt) {
  49. // 返回缓存的数据
  50. return data;
  51. }
  52. cache.delete(key); // 清除过期的缓存项
  53. }
  54. } catch (error) {
  55. console.error('在获取缓存数据时发生错误:', error);
  56. }
  57. // 如果缓存不存在或已过期,返回null
  58. return null;
  59. }
  60. // clearExpiredCache函数:清除过期的缓存
  61. function clearExpiredCache() {
  62. const now = Date.now(); // 获取当前时间
  63. cache.forEach((value, key) => {
  64. if (value && value.expireAt && value.expireAt < now) {
  65. cache.delete(key); // 如果过期,删除缓存项
  66. }
  67. });
  68. }
  69. // 设置一个定时器,每60秒执行一次清除过期的缓存
  70. setInterval(clearExpiredCache, 60000);
  71. // 返回设置数据和获取数据的方法
  72. return { setDataToCache, getDataFromCache };
  73. }

缓存的管理和优化不足

无论缓存是否被使用,都会定期执行清除操作,这可能会造成一些不必要的性能损耗。另外,它也没有考虑到缓存的容量问题,如果缓存中存储了过多的数据,可能会占用过多的内存空间。

考虑这些方面我们的解决方案是使用 setTimeout 函数来为每个缓存项设置一个单独的定时器,当缓存过期时自动清除,避免不必要的性能损耗。同时,可以使用 LRU(最近最少使用)算法来管理缓存的容量,当缓存达到一定的大小时,自动删除最久未使用的缓存项,避免缓存占用过多的空间。优化后的代码如下:

  1. import { reactive } from 'vue';
  2. // 缓存值的接口定义
  3. interface CacheValue {
  4. data: any; // 存储的数据
  5. expireAt: number; // 数据的过期时间戳
  6. timer?: any;
  7. }
  8. // 使用缓存的功能函数
  9. export function useCache() {
  10. // 创建一个响应式的Map对象来存储缓存
  11. const cache = reactive<Map<string, CacheValue>>(new Map());
  12. // 设置缓存的最大容量
  13. const max = 10;
  14. // 使用一个数组来存储缓存的键,按照最近使用的顺序排序
  15. const keys = [];
  16. /**
  17. * @param {string} key - 数据的键
  18. * @param {any} data - 要存储的数据
  19. * @param {number} cacheTime - 数据的缓存时间(以毫秒为单位)
  20. */
  21. function setDataToCache(key: string, data: any, cacheTime: number) {
  22. try {
  23. // 错误处理:确保所有参数都不为空
  24. if (!key || !data || !cacheTime) {
  25. throw new Error('参数不能为空');
  26. }
  27. // 错误处理:确保cacheTime是一个有效的正数字
  28. if (typeof cacheTime !== 'number' || isNaN(cacheTime) || cacheTime <= 0) {
  29. throw new Error('缓存时间必须是一个正数字');
  30. }
  31. // 计算过期时间戳
  32. const expireAt = Date.now() + cacheTime;
  33. // 将数据和过期时间存储到缓存中
  34. cache.set(key, { data, expireAt });
  35. // 为每个缓存项设置一个定时器,当缓存过期时自动清除
  36. const timer = setTimeout(() => {
  37. cache.delete(key);
  38. // 从键数组中移除该键
  39. keys.splice(keys.indexOf(key), 1);
  40. }, cacheTime);
  41. // 将定时器的引用也存储到缓存中,方便取消
  42. cache.get(key)!.timer = timer;
  43. // 将键添加到键数组的开头
  44. keys.unshift(key);
  45. // 如果缓存的数量超过了最大容量
  46. if (keys.length > max) {
  47. // 获取最久未使用的键
  48. const lastKey = keys.pop()!;
  49. // 清除该键对应的缓存项和定时器
  50. clearTimeout(cache.get(lastKey)!.timer);
  51. cache.delete(lastKey);
  52. }
  53. } catch (error) {
  54. // 在控制台输出错误信息,方便调试
  55. console.error('在设置缓存时发生错误:', error);
  56. // 可以根据需要向用户发出警告或执行其他错误处理逻辑
  57. }
  58. }
  59. /**
  60. *getDataFromCache函数:尝试从缓存中获取数据
  61. *@param {string} key - 数据的键
  62. *@returns {any|null} 如果缓存存在且未过期,返回缓存的数据;否则返回null
  63. */
  64. function getDataFromCache(key) {
  65. try {
  66. // 如果缓存中存在这个键
  67. if (cache.get(key)) {
  68. // 获取键对应的缓存对象
  69. const { data, expireAt } = cache.get(key) as CacheValue;
  70. // 如果当前时间还没有超过过期时间
  71. if (Date.now() < expireAt) {
  72. // 返回缓存的数据
  73. return data;
  74. }
  75. // 如果缓存已过期,清除缓存项和定时器
  76. cache.delete(key);
  77. clearTimeout(cache.get(key)!.timer);
  78. // 从键数组中移除该键
  79. keys.splice(keys.indexOf(key), 1);
  80. }
  81. } catch (error) {
  82. console.error('在获取缓存数据时发生错误:', error);
  83. }
  84. // 如果缓存不存在或已过期,返回null
  85. return null;
  86. }
  87. // 返回设置数据和获取数据的方法
  88. return { setDataToCache, getDataFromCache };
  89. }

清空缓存

除此之外,还缺少一个清空所有缓存的功能:

  1. function clearAllCache() {
  2. // 清空缓存中的所有数据
  3. cache.clear();
  4. // 取消所有的定时器
  5. cache.forEach((value) => {
  6. clearTimeout(value.timer);
  7. });
  8. // 清空键数组
  9. keys.length = 0;
  10. }

最终代码

  1. import { reactive } from 'vue';
  2. // 缓存值的接口定义
  3. interface CacheValue {
  4. data: any; // 存储的数据
  5. expireAt: number; // 数据的过期时间戳
  6. timer?: any;
  7. }
  8. // 使用缓存的功能函数
  9. export function useCache() {
  10. // 创建一个响应式的Map对象来存储缓存
  11. const cache = reactive<Map<string, CacheValue>>(new Map());
  12. // 设置缓存的最大容量
  13. const max = 10;
  14. // 使用一个数组来存储缓存的键,按照最近使用的顺序排序
  15. const keys = [];
  16. /**
  17. * @param {string} key - 数据的键
  18. * @param {any} data - 要存储的数据
  19. * @param {number} cacheTime - 数据的缓存时间(以毫秒为单位)
  20. */
  21. function setDataToCache(key: string, data: any, cacheTime: number) {
  22. try {
  23. // 错误处理:确保所有参数都不为空
  24. if (!key || !data || !cacheTime) {
  25. throw new Error('参数不能为空');
  26. }
  27. // 错误处理:确保cacheTime是一个有效的正数字
  28. if (typeof cacheTime !== 'number' || isNaN(cacheTime) || cacheTime <= 0) {
  29. throw new Error('缓存时间必须是一个正数字');
  30. }
  31. // 计算过期时间戳
  32. const expireAt = Date.now() + cacheTime;
  33. // 将数据和过期时间存储到缓存中
  34. cache.set(key, { data, expireAt });
  35. // 为每个缓存项设置一个定时器,当缓存过期时自动清除
  36. const timer = setTimeout(() => {
  37. cache.delete(key);
  38. // 从键数组中移除该键
  39. keys.splice(keys.indexOf(key), 1);
  40. }, cacheTime);
  41. // 将定时器的引用也存储到缓存中,方便取消
  42. cache.get(key)!.timer = timer;
  43. // 将键添加到键数组的开头
  44. keys.unshift(key);
  45. // 如果缓存的数量超过了最大容量
  46. if (keys.length > max) {
  47. // 获取最久未使用的键
  48. const lastKey = keys.pop()!;
  49. // 清除该键对应的缓存项和定时器
  50. clearTimeout(cache.get(lastKey)!.timer);
  51. cache.delete(lastKey);
  52. }
  53. } catch (error) {
  54. // 在控制台输出错误信息,方便调试
  55. console.error('在设置缓存时发生错误:', error);
  56. // 可以根据需要向用户发出警告或执行其他错误处理逻辑
  57. }
  58. }
  59. /**
  60. *getDataFromCache函数:尝试从缓存中获取数据
  61. *@param {string} key - 数据的键
  62. *@returns {any|null} 如果缓存存在且未过期,返回缓存的数据;否则返回null
  63. */
  64. function getDataFromCache(key) {
  65. try {
  66. // 如果缓存中存在这个键
  67. if (cache.get(key)) {
  68. // 获取键对应的缓存对象
  69. const { data, expireAt } = cache.get(key) as CacheValue;
  70. // 如果当前时间还没有超过过期时间
  71. if (Date.now() < expireAt) {
  72. // 返回缓存的数据
  73. return data;
  74. }
  75. // 如果缓存已过期,清除缓存项和定时器
  76. cache.delete(key);
  77. clearTimeout(cache.get(key)!.timer);
  78. // 从键数组中移除该键
  79. keys.splice(keys.indexOf(key), 1);
  80. }
  81. } catch (error) {
  82. console.error('在获取缓存数据时发生错误:', error);
  83. }
  84. // 如果缓存不存在或已过期,返回null
  85. return null;
  86. }
  87. function clearAllCache() {
  88. // 清空缓存中的所有数据
  89. cache.clear();
  90. // 取消所有的定时器
  91. cache.forEach((value) => {
  92. clearTimeout(value.timer);
  93. });
  94. // 清空键数组
  95. keys.length = 0;
  96. }
  97. // 返回设置数据和获取数据的方法
  98. return { setDataToCache, getDataFromCache, clearAllCache };
  99. }

以上便是一些可优化的点,除此之外,你还可以从持久化、性能监控、并发控制 、缓存策略等方面优化。

结语 

在本篇文章中,我们探讨了Vue缓存Hook的使用,这是一个基于Vue响应式系统的缓存工具,旨在帮助我们在组件之间轻松地共享和管理缓存数据。通过使用这个缓存Hook,我们能够有效地提高应用性能,减少不必要的接口调用,从而提升用户体验。希望这篇文章能够帮到大家,特别是在开发Vue应用时需要考虑性能优化和缓存管理的朋友们。如果你有任何问题或疑问,欢迎随时提问,我会尽力帮助解答。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/82675
推荐阅读
相关标签
  

闽ICP备14008679号