17、前端缓存设计全攻略:本地缓存 + 接口缓存

🪜 写在前面

缓存设计的好坏,直接决定前端系统的性能上限用户体验下限

一个优秀的架构师(还不是没关系 先拥抱思想)需要理解并合理运用以下缓存层:

层级 作用 生命周期
Memory 缓存 页面内高频数据复用 页面生命周期
localStorage/sessionStorage 跨页面共享、快速读取 永久 / 会话
IndexedDB 大体积、结构化数据 长期
接口缓存层 减少请求压力、接口回源 配置化 / TTL
HTTP 缓存 CDN/browser 缓存响应 浏览器策略控制

本篇将系统梳理前端所有可用缓存策略,并结合实战封装一个统一的缓存工具库请求缓存机制


🧱 一、缓存分层模型图

javascript 复制代码
                     ┌──────────────────┐
                     │    IndexedDB     │  ← 大体积 / 富媒体缓存
                     └────────┬─────────┘
                              │
                    ┌─────────▼────────┐
                    │ localStorage/SS  │  ← 跨页面持久化缓存
                    └─────────┬────────┘
                              │
                  ┌───────────▼───────────┐
                  │     Memory Cache     │  ← 当前页面状态共享
                  └───────────┬──────────┘
                              │
                     ┌────────▼────────┐
                     │    接口缓存层    │  ← 请求封装统一管理
                     └─────────────────┘

✅ 二、封装一个统一的缓存工具库

typescript 复制代码
// cache/index.ts
export const cache = {
  memory: new Map<string, any>(),

  setLocal(key: string, value: any) {
    localStorage.setItem(key, JSON.stringify(value))
  },

  getLocal<T>(key: string): T | null {
    const raw = localStorage.getItem(key)
    try {
      return raw ? JSON.parse(raw) : null
    } catch {
      return null
    }
  },

  setSession(key: string, value: any) {
    sessionStorage.setItem(key, JSON.stringify(value))
  },

  getSession<T>(key: string): T | null {
    const raw = sessionStorage.getItem(key)
    try {
      return raw ? JSON.parse(raw) : null
    } catch {
      return null
    }
  },

  clear(key?: string) {
    if (key) {
      localStorage.removeItem(key)
      sessionStorage.removeItem(key)
      this.memory.delete(key)
    } else {
      localStorage.clear()
      sessionStorage.clear()
      this.memory.clear()
    }
  },
}

🔁 三、接口缓存封装(支持 TTL)

typescript 复制代码
// cache/requestCache.ts
const requestCache = new Map<string, { data: any; expire: number }>()

export async function cachedRequest<T>(
  key: string,
  requestFn: () => Promise<T>,
  ttl = 5000
): Promise<T> {
  const now = Date.now()
  const cacheItem = requestCache.get(key)

  if (cacheItem && now < cacheItem.expire) {
    return cacheItem.data
  }

  const data = await requestFn()
  requestCache.set(key, { data, expire: now + ttl })
  return data
}

使用方式:

csharp 复制代码
const userInfo = await cachedRequest('userInfo', getUserInfo, 30000)

📦 四、应用场景实战示例

🎯 场景1:分页接口"翻页返回"不重复请求

javascript 复制代码
const cacheKey = `goods?page=${page}&size=${size}`
const goodsList = await cachedRequest(cacheKey, () => getGoodsList({ page, size }), 60000)

🎯 场景2:配置类接口(如"角色列表"、"系统配置")

csharp 复制代码
const roles = await cachedRequest('static_roles', fetchRoles, 5 * 60 * 1000)

🎯 场景3:首页推荐数据缓存(Memory + Fallback to Storage)

vbnet 复制代码
const key = 'homepageRecommend'
let data = cache.memory.get(key) || cache.getLocal(key)

if (!data) {
  data = await fetchHomepage()
  cache.memory.set(key, data)
  cache.setLocal(key, data)
}

🧠 五、IndexedDB 使用场景

场景 推荐使用
大型资源文件(图片、视频、压缩包)
富文本内容离线保存
文件上传分片缓存
高并发结构化数据(日志流、埋点)

使用库推荐:

  • idb(最简洁,Google 出品)
  • localForage(封装层丰富)
  • 自定义封装 indexedDB 的 store.open()

🔐 六、缓存安全与有效性策略

问题 建议方案
多用户切换缓存污染 缓存 key 添加 userId 前缀
数据更新后缓存未同步 在接口响应中返回 version/hash 与本地对比
过期缓存未清除 TTL 控制 + 定期清理任务
本地缓存中数据结构变动 版本标识 + 缓存结构迁移方案

⚡ 七、缓存与离线能力结合

可以扩展支持 PWA/Service Worker 缓存:

javascript 复制代码
// sw.js
self.addEventListener('fetch', (event) => {
  if (event.request.method !== 'GET') return

  event.respondWith(
    caches.match(event.request).then((cachedRes) => {
      return cachedRes || fetch(event.request).then((res) => {
        const resClone = res.clone()
        caches.open('my-cache').then((cache) => cache.put(event.request, resClone))
        return res
      })
    })
  )
})

📈 八、缓存监控与调试建议

  • Chrome DevTools → Application 面板查看 localStorage、sessionStorage、IndexedDB

  • 添加缓存命中/写入 log(用于调试性能):

    javascript 复制代码
    console.log(`[CACHE] HIT: ${key}`)
  • 后端响应中返回 X-Cache-Status: HIT/MISS

  • 开发环境中提供"清除缓存"按钮/快捷键


✅ 九、最佳实践清单

项目 建议
接口级缓存 使用 cachedRequest 包装请求,设置合理 TTL
静态配置缓存 使用 localStorage,搭配版本号判断是否更新
多页面共享数据 使用 sessionStorage,或 window.name 跨 iframe
大文件缓存 IndexedDB 并做清理策略
UI 状态缓存 使用 memory cache,组件卸载时清空
用户级缓存隔离 所有 key 添加 user_id 维度

🧠 总结一句话

缓存不是"为了省请求",而是"为了体验稳定、数据安全、系统弹性"。


下一篇我们进入「测试体系建设」阶段:

👉 《组件测试、集成测试与 E2E:前端测试体系构建》

相关推荐
GoldKey2 分钟前
gcc 源码阅读---语法树
linux·前端·windows
Xf3n1an1 小时前
html语法
前端·html
张拭心1 小时前
亚马逊 AI IDE Kiro “狙击”Cursor?实测心得
前端·ai编程
漠月瑾-西安1 小时前
如何在 React + TypeScript 中实现 JSON 格式化功能
javascript·jst实现json格式化
烛阴1 小时前
为什么你的Python项目总是混乱?层级包构建全解析
前端·python
止观止2 小时前
React响应式组件范式:从类组件到Hooks
javascript·react.js·ecmascript
@大迁世界2 小时前
React 及其生态新闻 — 2025年6月
前端·javascript·react.js·前端框架·ecmascript
LJianK12 小时前
Java和JavaScript的&&和||
java·javascript·python
红尘散仙3 小时前
Rust 终端 UI 开发新玩法:用 Ratatui Kit 轻松打造高颜值 CLI
前端·后端·rust
新酱爱学习3 小时前
前端海报生成的几种方式:从 Canvas 到 Skyline
前端·javascript·微信小程序