Vue3选项式API:Axios缓存Rest API数据与避免重复请求方案
Vue 3选项式API中Axios缓存REST API数据并避免重复请求的方案
在Vue 3选项式API里,实现Axios请求数据的缓存并避免重复请求,核心思路是请求前先检查缓存,有数据直接用,无数据再发起请求并把结果存入缓存。下面分两种常见缓存场景给出具体实现:
一、内存缓存(会话级,页面刷新后失效)
适合不需要持久化的场景,比如单页应用内的临时缓存。
1. 组件内缓存实现
在组件内部维护缓存状态和请求锁,避免重复触发请求:
<template> <div>{{ cachedData ? JSON.stringify(cachedData) : '加载中...' }}</div> </template> <script> import axios from 'axios' export default { data() { return { cachedData: null, isFetching: false // 请求锁,防止同一时间重复发起请求 } }, async mounted() { await this.fetchDataWithCache() }, methods: { async fetchDataWithCache() { // 先检查内存缓存 if (this.cachedData) { console.log('使用内存缓存数据') return } // 检查是否已有请求在进行 if (this.isFetching) return this.isFetching = true try { const response = await axios.get('https://api.example.com/data') this.cachedData = response.data console.log('请求成功,已存入内存缓存') } catch (error) { console.error('请求失败:', error) } finally { this.isFetching = false } } } } </script>
2. 全局内存缓存(多组件共享)
如果多个组件需要共用同一接口数据,可以封装一个全局缓存工具:
// utils/axiosCache.js import axios from 'axios' const memoryCache = new Map() const pendingRequests = new Set() export async function fetchWithMemoryCache(url) { // 检查缓存是否存在 if (memoryCache.has(url)) { return memoryCache.get(url) } // 检查是否已有相同请求在处理 if (pendingRequests.has(url)) { // 等待已有请求完成后返回缓存数据 return new Promise(resolve => { const interval = setInterval(() => { if (memoryCache.has(url)) { clearInterval(interval) resolve(memoryCache.get(url)) } }, 50) }) } pendingRequests.add(url) try { const response = await axios.get(url) memoryCache.set(url, response.data) return response.data } catch (error) { throw error } finally { pendingRequests.delete(url) } }
组件内使用示例:
<script> import { fetchWithMemoryCache } from '@/utils/axiosCache' export default { data() { return { cachedData: null } }, async mounted() { try { this.cachedData = await fetchWithMemoryCache('https://api.example.com/data') } catch (error) { console.error(error) } } } </script>
二、浏览器本地缓存(持久化,页面刷新后仍保留)
用localStorage实现持久化缓存,建议加上过期时间避免缓存过期数据:
封装带过期时间的本地缓存请求
// utils/axiosLocalCache.js import axios from 'axios' const CACHE_KEY_PREFIX = 'api_cache_' // 从localStorage读取缓存并检查有效期 function getLocalCache(key) { const cacheStr = localStorage.getItem(CACHE_KEY_PREFIX + key) if (!cacheStr) return null const cacheObj = JSON.parse(cacheStr) if (Date.now() > cacheObj.expireTime) { localStorage.removeItem(CACHE_KEY_PREFIX + key) return null } return cacheObj.data } // 将数据存入localStorage并设置过期时间 function setLocalCache(key, data, expireMinutes = 30) { const cacheObj = { data, expireTime: Date.now() + expireMinutes * 60 * 1000 } localStorage.setItem(CACHE_KEY_PREFIX + key, JSON.stringify(cacheObj)) } export async function fetchWithLocalCache(url, expireMinutes = 30) { const cacheKey = url // 可根据请求参数生成更唯一的key,比如`url+JSON.stringify(params)` const cachedData = getLocalCache(cacheKey) if (cachedData) { console.log('使用本地缓存数据') return cachedData } try { const response = await axios.get(url) setLocalCache(cacheKey, response.data, expireMinutes) console.log('请求成功,已存入本地缓存') return response.data } catch (error) { throw error } }
组件内使用本地缓存请求
<template> <div>{{ cachedData ? JSON.stringify(cachedData) : '加载中...' }}</div> </template> <script> import { fetchWithLocalCache } from '@/utils/axiosLocalCache' export default { data() { return { cachedData: null } }, async mounted() { try { // 设置缓存有效期为10分钟 this.cachedData = await fetchWithLocalCache('https://api.example.com/data', 10) } catch (error) { console.error('请求失败:', error) } } } </script>
三、Axios拦截器全局实现缓存
如果想给所有GET请求统一加缓存逻辑,可以通过拦截器实现:
// axios.js import axios from 'axios' const memoryCache = new Map() const pendingRequests = new Set() const instance = axios.create({ baseURL: 'https://api.example.com' }) // 请求拦截器:检查缓存,存在则取消原请求返回缓存 instance.interceptors.request.use(config => { if (config.method?.toLowerCase() === 'get') { const cacheKey = config.url + JSON.stringify(config.params) if (memoryCache.has(cacheKey)) { const source = axios.CancelToken.source() config.cancelToken = source.token source.cancel('使用缓存数据,取消原请求') return Promise.resolve({ data: memoryCache.get(cacheKey) }) } if (pendingRequests.has(cacheKey)) { const source = axios.CancelToken.source() config.cancelToken = source.token source.cancel('已有相同请求在处理,取消当前请求') } else { pendingRequests.add(cacheKey) } } return config }) // 响应拦截器:将请求结果存入缓存 instance.interceptors.response.use(response => { if (response.config.method?.toLowerCase() === 'get') { const cacheKey = response.config.url + JSON.stringify(response.config.params) memoryCache.set(cacheKey, response.data) pendingRequests.delete(cacheKey) } return response }, error => { if (axios.isCancel(error)) { console.log(error.message) return Promise.resolve({ data: memoryCache.get(error.config.url + JSON.stringify(error.config.params)) }) } pendingRequests.delete(error.config.url + JSON.stringify(error.config.params)) return Promise.reject(error) }) export default instance
组件内使用封装好的Axios实例:
<script> import axios from '@/axios' export default { data() { return { cachedData: null } }, async mounted() { try { const response = await axios.get('/data', { params: { id: 1 } }) this.cachedData = response.data } catch (error) { console.error(error) } } } </script>
内容的提问来源于stack exchange,提问作者Uzzal Hossain
相关产品推荐
相关产品推荐

