LocalStorage无法原生实现LRU,需手动维护cache:data:key存值、cache:index数组记录访问顺序,每次读写将key置顶,超限时从末尾删除最久未用项,并通过封装类统一管理存取与清理逻辑。

怎么用 localStorage 模拟 LRU 缓存结构
不能直接靠 localStorage 自带机制实现 LRU,必须手动维护访问顺序。核心是两个元数据:一个存实际值(cache:data:key),一个存 key 的使用时序(cache:index)。
每次 set() 或 get() 都要更新 cache:index 数组——把对应 key 移到开头,其余保持相对顺序。数组越长越靠后,就越“久未使用”。
-
set(key, value)里先JSON.stringify(value)存入cache:data:key,再更新 index; -
get(key)读完值后,必须重新写回 index(否则下次 prune 会误删); - index 更新别用
splice()+unshift()组合,容易漏掉重复 key,推荐filter().unshift()安全去重; - 首次读
cache:index为空或解析失败时,应 fallback 为[],避免整个缓存失效。
为什么不能只按条目数清理,而要估算字节大小
localStorage 的容量限制是 5MB(多数浏览器),但 localStorage.length 只返回 key 的个数,完全不反映实际占用。一个大 JSON 对象可能占几百 KB,而十个短字符串才几十字节。
真正有效的清理逻辑,得基于每个 key 的实际存储开销。用 encodeURIComponent(JSON.stringify(value)).length 可较准确估算 UTF-8 字节数(比 JSON.stringify(value).length 更可靠)。
立即学习“前端免费学习笔记(深入)”;
- 建议把每个 key 的 size 单独存在
cache:size:key,或聚合在cache:sizes对象里; - prune 时优先删 “size 大 + index 位置靠后” 的项,比单纯删末尾更高效;
- 别在每次
set()后都 full-scan 所有 key 计算总大小,可设阈值(如 >4.5MB)再触发精确计算。
Service Worker 和 localStorage 缓存结构的根本区别
Service Worker 的缓存(caches.open('xxx'))是真正的键值对资源缓存,能存完整 Response 对象,支持 URL 作为 key、自动处理 MIME 类型和状态码;localStorage 只能存字符串,所有 value 必须序列化,且无过期、无 HTTP 语义、无跨域隔离。
- HTML 页面本身适合用
Service Worker缓存(配合cache.match()+fetch()fallback); - 用户偏好、表单草稿、临时 token 这类小结构化数据,更适合
localStorage+ LRU 封装; - 两者不互斥:可用
Service Worker管 HTML/CSS/JS,localStorage管运行时状态,但别把大文件 dump 进localStorage—— 超限会静默失败,且无法捕获。
HTML 文件本身要不要进 localStorage 缓存
不要。HTML 是主入口文档,浏览器已有成熟的 HTTP 缓存机制(Cache-Control、ETag),强行塞进 localStorage 会绕过协商缓存、破坏 304 流程,还增加 JS 解析开销和内存压力。
- 如果目标是离线可用,用
Service Worker拦截navigation请求并返回缓存 HTML; - 如果只是想加速首屏,优先优化服务器响应头(如
Cache-Control: public, max-age=300),再配 ETag; - 真要用
localStorage存 HTML 片段,仅限极小的、纯静态的模板(如弹窗 HTML 字符串),且必须加版本前缀(template:v2:login)防 stale。
get() 但没更新 index,或者异步操作中 set() 成功但 index 更新被丢弃,都会导致缓存污染。这类问题不会报错,只会慢慢让 prune 失效。



















