存储持久化、配额与逐出
一句话: 浏览器为每个源在 Cache Storage、IndexedDB 等之间分配一个共享配额,当磁盘
吃紧时优先逐出尽力而为(best-effort)的数据。navigator.storage.estimate() 告诉你
用了多少;persist() 申请把你的源提升为持久(persistent),从而免于自动逐出。
尽力而为 vs 持久存储
Section titled “尽力而为 vs 持久存储”- 尽力而为是默认模式。数据正常情况下会保留,但在存储压力下,浏览器可能不加警告地 逐出你的整个源以回收空间。
- 持久存储需通过
navigator.storage.persist()主动申请。持久的源不会被自动逐出 ——其数据只有在用户显式清除时才会被删除。 - 逐出对每个源是全有或全无:浏览器清空整个源的存储箱,而非单条记录。尽力而为的源最先被 逐出,其中又以最久未使用者优先。
哪些算入配额
Section titled “哪些算入配额”单一的每源配额由各存储 API 共享:
- Cache Storage(service worker 缓存的离线应用外壳与资源)
- IndexedDB(结构化应用数据、大型 blob)
- service worker 注册脚本,以及遗留存储(localStorage、应用提供的文件)
配额是可用磁盘空间的一部分,而非固定数值——它随磁盘填满而收缩,并因浏览器而异。把它 当作弹性的,永远不要假设有硬性上限。
估算与申请持久化
Section titled “估算与申请持久化”// 已用量与当前上限(字节)。const { usage, quota } = await navigator.storage.estimate();
// 该源是否已持久?const isPersisted = await navigator.storage.persisted();
// 申请提升。被授予时解析为 true。if (!isPersisted) { const granted = await navigator.storage.persist();}persist() 返回解析为布尔值的 promise——永远不要假设它会成功。请用 persisted()
重新检查,而非缓存结果,因为浏览器策略可能改变。
浏览器如何决定授予持久化
Section titled “浏览器如何决定授予持久化”浏览器依据“用户重视此站点”的信号来授予持久化——没有单一开关,且各引擎行为不同:
| 信号 | 对授予的影响 |
|---|---|
| PWA 已安装到主屏 / 操作系统 | 最强信号;持久化通常自动或一经申请即被授予。 |
| 已授予通知权限 | 往往单凭此项就足以让浏览器授予持久化。 |
| 高站点参与度(频繁访问、加书签) | 可将决策推向授予的启发式因素。 |
| 首次访问的匿名标签页、低参与度 | 很可能被拒;待参与度积累后再次申请。 |
| Firefox | 直接向用户弹出持久化权限提示。 |
- 为任何离线关键型 PWA(离线草稿、排队的变更、缓存媒体)调用
persist()。 - 在有意义的操作或安装之后申请持久化——而非首屏绘制时——以便存在参与度信号。
- 始终处理
persist()返回false的情况并优雅降级;不要因它而阻塞 UI。 - 大量写入前调用
estimate();若usage接近quota,先清理旧缓存。 - 为 IndexedDB 与 Cache 写入包裹捕获
QuotaExceededError,随后逐出陈旧数据并重试。 - 永远不要把配额当作固定值——用重新
estimate()取代硬编码的容量预算。 - 使用前特性检测
navigator.storage;较旧浏览器缺少 StorageManager API。
这对信任意味着什么
Section titled “这对信任意味着什么”持久化存储决定了一个已安装的 PWA 是能可靠地离线打开,还是会在磁盘吃紧时悄无声息地丢失
用户排队的数据。仅在真实参与之后再申请,可让授予更可能、提示不扰人;而优雅处理
QuotaExceededError 则意味着磁盘满载时体验降级而非数据损坏——这正是用户对自己主动安装
的东西所期待的持久性。