Vue 3中请求缓存应由组合式函数实现,拦截器仅负责统一注入、错误处理及可观测性支持;缓存键生成、TTL管理、失效控制等业务逻辑均由composable封装,确保可复用、可配置、可测试。

Vue 3 中请求拦截器本身不直接管理缓存命中,它只负责在请求发出前或响应返回后执行逻辑。真正实现“请求缓存命中”需要组合式函数(composable)来封装缓存策略,并与拦截器协同分工:拦截器专注统一注入、错误处理、加载状态;缓存逻辑则由组合式函数按需调用、读写、失效。
缓存职责应交给组合式函数,而非拦截器
拦截器运行在 axios 实例级别,是全局、无上下文的管道。它不知道某次请求是否该走缓存、缓存键怎么生成、过期时间如何设定——这些都依赖业务语义,必须由调用方(组件或业务逻辑)明确决定。组合式函数天然适合承载这类有状态、可复用、可配置的逻辑:
- 缓存键可基于 URL + 参数序列化生成,支持 deepEqual 或 JSON.stringify 规范化
- 缓存存储可用 reactive Map 或 ref + localStorage(带过期时间)
- 提供 clearCache、invalidateByPattern 等可控 API,便于权限变更、数据更新时主动失效
- 返回的 Promise 可包装为“先查缓存,未命中再发请求”,对组件透明
拦截器配合缓存的典型协作方式
拦截器不替代缓存,但能为缓存提供基础设施支持:
-
请求阶段:拦截器可统一添加 cache-control header(如
cache: 'no-cache'),避免浏览器或 CDN 干扰应用层缓存逻辑 -
响应阶段:拦截器可识别特定响应头(如
X-Cache-Hit: true),触发全局事件或更新 Pinia 缓存统计状态,供监控面板使用 - 错误兜底:当网络请求失败,拦截器可检查本地缓存是否存在有效数据,自动 fallback 并标记“降级响应”,提升用户体验
一个轻量缓存组合式函数示例
以下是一个基于 ref + Map 的简易缓存 composable,支持 TTL 和手动清除:
src/composables/useCachedRequest.tsimport { ref, computed } from 'vue'
import { axiosInstance } from '@/api/http/client'
<p>interface CacheItem<t> {
data: T
timestamp: number
ttl: number // ms
}</t></p><p>const cache = new Map<string cacheitem>>()</string></p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5039" title="check-vue-style"><img
src="https://img.php.cn/upload/skill/000/000/081/179032142844293.jpg" alt="check-vue-style" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill5039" title="check-vue-style" class="overflowclass">check-vue-style</a>
<p class="overflowclass">检查 .vue 单文件组件的 CSS 规范问题并给出提示,适用于样式检查、CSS审查和 Vue 页面质量巡检。</p>
</div>
<a rel="nofollow" href="/xiazai/skill5039" title="check-vue-style" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><p>export function useCachedRequest<t>(key: string, fetcher: () => Promise<t>, options: { ttl?: number } = {}) {
const { ttl = 5 <em> 60 </em> 1000 } = options // 默认5分钟
const loading = ref(false)
const error = ref<error null>(null)</error></t></t></p><p>const cached = computed(() => {
const item = cache.get(key)
if (!item) return null
if (Date.now() - item.timestamp > item.ttl) {
cache.delete(key)
return null
}
return item.data
})</p><p>const execute = async (): Promise<t> => {
if (cached.value) return cached.value</t></p><pre class="brush:php;toolbar:false;">loading.value = true
error.value = null
try {
const data = await fetcher()
cache.set(key, { data, timestamp: Date.now(), ttl })
return data
} catch (e) {
error.value = e instanceof Error ? e : new Error('请求失败')
throw e
} finally {
loading.value = false
}}
const clear = () => cache.delete(key)
return { data: cached, loading, error, execute, clear } }
拦截器中不写缓存逻辑,但可增强其可观测性
你可以在响应拦截器里加入一行日志或事件通知,让缓存行为更可追踪:
src/api/http/interceptors/index.ts// 响应拦截器中
instance.interceptors.response.use(
response => {
// 若后端返回了缓存命中标识,记录埋点
if (response.headers['x-cache'] === 'HIT') {
console.debug(`[Cache] ${response.config.url} served from cache`)
// 或触发 Pinia store 更新 hitCount
}
return response
},
error => { /* 统一错误处理 */ }
)
这样既保持拦截器的纯粹性,又让缓存策略在组合式函数中清晰、可测试、可复用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










