🌐 从 Map 到 LRUCache:构建智能缓存工厂函数

在现代前端与服务端同构项目中(如 Vite、Next.js、Nuxt 等),缓存系统的设计至关重要。下面这段简短的 TypeScript 代码展示了一种**自动适配运行环境(浏览器 / Node)**的缓存工厂函数:

typescript 复制代码
import { LRUCache } from 'lru-cache'

export function createCache<T extends {}>(
  max = 500,
): Map<string, T> | LRUCache<string, T> {
  /* v8 ignore next 3 */
  if (__GLOBAL__ || __ESM_BROWSER__) {
    return new Map<string, T>()
  }
  return new LRUCache({ max })
}

一、概念:缓存与 LRU 的核心思想

**缓存(Cache)**的本质是以空间换时间:

  • 通过暂存结果,减少重复计算或 I/O 操作。
  • 提升响应速度与资源利用效率。

**LRU(Least Recently Used)**缓存算法是一种"时间局部性"策略:

  • 每次访问某个键时,它会被标记为"最新使用"。
  • 当缓存空间不足时,会优先淘汰最久未访问的键值。

二、原理:代码逻辑逐行解析

javascript 复制代码
import { LRUCache } from 'lru-cache'

👉 从 lru-cache 包导入 LRUCache 类。

这是一个高性能、Node 端常用的缓存实现,内部使用链表维护访问顺序。

javascript 复制代码
export function createCache<T extends {}>(max = 500)

👉 定义一个泛型函数 createCache,返回类型为 Map<string, T>LRUCache<string, T>

默认最大缓存项为 500 条。

typescript 复制代码
if (__GLOBAL__ || __ESM_BROWSER__) {
  return new Map<string, T>()
}

👉 判断当前环境是否为浏览器端(例如通过 Vite 内置的全局标识 __ESM_BROWSER__)。

在浏览器中不能使用 Node 的 LRU 实现,因此退化为普通 Map 对象。

💡 Map 本身是有序的(按插入顺序),但不具备自动淘汰机制,因此只适用于轻量场景。

arduino 复制代码
return new LRUCache({ max })

👉 在 Node 环境下,返回一个真正的 LRUCache 实例。

每当缓存超过 max,它会自动移除最早未使用的键。


三、对比:Map vs LRUCache

特性 Map LRUCache
环境兼容性 ✅ 浏览器/Node 通用 ⚙️ Node 环境
自动过期机制 ❌ 无 ✅ 有(LRU 策略)
空间限制 ❌ 无 ✅ 可设定 max
性能(小缓存) 🚀 快速 稍慢但更智能
使用场景 前端暂存、组件状态 服务器端请求缓存、构建缓存

总结一句话:

Map 是"轻量记忆",LRUCache 是"带遗忘功能的智能记忆"。


四、实践:在同构项目中应用

假设我们正在开发一个 SSR 项目(例如 VitePress),希望在浏览器与 Node 中使用相同接口的缓存机制。

c 复制代码
const cache = createCache<{ title: string }>()

cache.set('article-1', { title: 'Understanding Caching' })
console.log(cache.get('article-1'))

在浏览器端:

  • createCache() 返回 Map
  • 无自动过期机制,但支持简单的 .set / .get 操作

在 Node 端:

  • createCache() 返回 LRUCache
  • 缓存条目达到上限后会自动回收旧数据

五、拓展:增强版的通用缓存工厂

你可以在此基础上增加缓存时间(TTL)命中统计

typescript 复制代码
export function createSmartCache<T>(
  max = 500,
  ttl = 1000 * 60 * 5 // 5 minutes
): LRUCache<string, T> | Map<string, T> {
  if (__GLOBAL__ || __ESM_BROWSER__) {
    return new Map<string, T>()
  }
  return new LRUCache<string, T>({ max, ttl })
}

👉 ttl 参数让缓存项在过期后自动失效。

适用于 API 缓存、构建元数据缓存等场景。


六、潜在问题与优化方向

  1. 浏览器端的 Map 缓存无清理机制

    长时间使用可能导致内存膨胀,建议手动清空或限制使用周期。

  2. 类型不统一问题

    函数返回类型是联合类型(Map | LRUCache),在 TS 中使用时需要进行类型收窄:

    scss 复制代码
    if (cache instanceof LRUCache) {
      cache.dump()
    }
  3. 环境变量依赖
    __GLOBAL____ESM_BROWSER__ 通常由打包器(如 Vite)注入;若自定义构建流程,需要手动定义。

  4. 更通用的抽象

    可进一步封装为类或接口,以统一 .get() / .set() API,屏蔽底层差异。


七、结语

这段简洁的代码其实体现了优秀的环境适配设计哲学

"同一个接口,不同平台下提供最合理的实现。"

它在构建工具、插件系统或 SSR 场景中非常常见,是一种值得借鉴的"环境感知工厂模式"。


本文部分内容借助 AI 辅助生成,并由作者整理审核。

相关推荐
烛阴10 小时前
从`new()`到`.DoSomething()`:一篇讲透C#方法与构造函数的终极指南
前端·c#
还债大湿兄10 小时前
阿里通义千问调用图像大模型生成轮动漫风格 python调用
开发语言·前端·python
谢尔登11 小时前
defineProperty如何弥补数组响应式不足的缺陷
前端·javascript·vue.js
蓝瑟忧伤11 小时前
前端技术新十年:从工程体系到智能化开发的全景演进
前端
Baklib梅梅11 小时前
员工手册:保障运营一致性与提升组织效率的核心载体
前端·ruby on rails·前端框架·ruby
IT_陈寒12 小时前
Redis性能翻倍的5个冷门技巧,90%开发者都不知道第3个!
前端·人工智能·后端
jingling55513 小时前
vue | 在 Vue 3 项目中集成高德地图(AMap)
前端·javascript·vue.js
油丶酸萝卜别吃13 小时前
Vue3 中如何在 setup 语法糖下,通过 Layer 弹窗组件弹出自定义 Vue 组件?
前端·vue.js·arcgis
J***Q29220 小时前
Vue数据可视化
前端·vue.js·信息可视化
ttod_qzstudio21 小时前
深入理解 Vue 3 的 h 函数:构建动态 UI 的利器
前端·vue.js