很多同学一听到 LRU 缓存,第一反应就是“双向链表 + 哈希表”那道经典面试题。但在 Open UI5 这种企业级框架里,LRU 不是拿来应付面试的,而是要实打实地解决“跨会话重复计算”的性能问题。这篇我来拆一下 LRUPersistentCache.js 这个模块,搞清楚它在 Open UI5 里的定位、实现逻辑,以及实际使用中那些文档里不会写的问题。
先说结论:LRUPersistentCache.js 做的事情,是把 LRU(Least Recently Used)淘汰策略和浏览器持久化存储结合在一起,让一部分“算出来一次就能用很久”的数据,在页面刷新甚至浏览器重启之后还能直接复用,而不是每次启动都重新生成一遍。
1. 缓存为什么非要“持久化”:性能瓶颈到底在哪
1.1 从 LRUCache 说起:进程内缓存解决什么问题,解决不了什么
Open UI5 框架内部本来就有内存版的 LRUCache,它的作用和所有内存缓存一样:在页面运行期间,保存一些访问频率高、计算代价大的结果,避免反复执行同样的逻辑。
但内存缓存有一个天然的边界——页面一刷新,全没了。对于一次页面会话内的重复操作来说,这没问题;但对于“每次进入应用都要重复生成”的数据,内存缓存就无能为力了。
举个典型场景:一个复杂的企业级 Fiori 应用,启动时要解析库元数据、计算主题参数、汇总资源清单。这些数据本身是稳定的,不会因为用户刷新一次页面就变化。如果不做持久化,用户每次打开应用都要把这些事情重新做一遍。哪怕单次只要几百毫秒,放大到每次会话、每个用户,成本就很可观。
1.2 PersistentCache 在 Open UI5 源码中的位置与职责
LRUPersistentCache.js 在 Open UI5 里的定位,就是“带淘汰策略的跨会话存储”。它和普通 localStorage 直接读写的区别在于:
- 有容量上限,超过上限会按 LRU 顺序淘汰旧数据;
- 有结构化的读写接口,不要求使用方关心序列化细节;
- 带有过期时间(TTL)概念,可以控制缓存数据的有效周期;
- 对存储异常做了容错处理,不会因为缓存损坏导致应用崩溃。
换句话说,它是在 localStorage/sessionStorage 之上又包了一层“缓存管理逻辑”。package 里如果只看 Cache.js、Storage.js,你会发现 Cache 负责纯内存逻辑,Storage 负责底层持久化存取,而 LRUPersistentCache 是把两者的优点拼在一起。
注意,这里说的“持久化”不是数据库级别的持久化,而是相对页面生命周期而言的持久化——只要浏览器存储还在,这些缓存数据就在。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 读源码前必须建立的模型:LRU 数据结构与淘汰语义
2.1 双向链表 + Map 的经典组合,还是 Map 的边删除边插入技巧?
大学里讲 LRU 基本都是“哈希表 + 双向链表”,但 Open UI5 的实现其实更偏向一种简化写法:用 Map 模拟 LRU 语义。
如果你要自己实现一个最小版,核心逻辑是这样的:
javascript复制class LRUCache {
constructor(limit) {
this.limit = limit;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) {
return null;
}
// 读取即刷新:先删掉再重新插入,把 key 移到 Map 末端
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.limit) {
// Map 的迭代顺序就是插入顺序,第一个 key 就是最久未使用的
this.cache.delete(this.cache.keys().next(
