跳转到内容

存储持久化、配额与逐出

一句话: 浏览器为每个源在 Cache Storage、IndexedDB 等之间分配一个共享配额,当磁盘 吃紧时优先逐出尽力而为(best-effort)的数据。navigator.storage.estimate() 告诉你 用了多少;persist() 申请把你的源提升为持久(persistent),从而免于自动逐出。

  • 尽力而为是默认模式。数据正常情况下会保留,但在存储压力下,浏览器可能不加警告地 逐出你的整个源以回收空间。
  • 持久存储需通过 navigator.storage.persist() 主动申请。持久的源不会被自动逐出 ——其数据只有在用户显式清除时才会被删除。
  • 逐出对每个源是全有或全无:浏览器清空整个源的存储箱,而非单条记录。尽力而为的源最先被 逐出,其中又以最久未使用者优先。

单一的每源配额由各存储 API 共享:

  • Cache Storage(service worker 缓存的离线应用外壳与资源)
  • IndexedDB(结构化应用数据、大型 blob)
  • service worker 注册脚本,以及遗留存储(localStorage、应用提供的文件)

配额是可用磁盘空间的一部分,而非固定数值——它随磁盘填满而收缩,并因浏览器而异。把它 当作弹性的,永远不要假设有硬性上限。

// 已用量与当前上限(字节)。
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() 重新检查,而非缓存结果,因为浏览器策略可能改变。

浏览器依据“用户重视此站点”的信号来授予持久化——没有单一开关,且各引擎行为不同:

信号 对授予的影响
PWA 已安装到主屏 / 操作系统 最强信号;持久化通常自动或一经申请即被授予。
已授予通知权限 往往单凭此项就足以让浏览器授予持久化。
高站点参与度(频繁访问、加书签) 可将决策推向授予的启发式因素。
首次访问的匿名标签页、低参与度 很可能被拒;待参与度积累后再次申请。
Firefox 直接向用户弹出持久化权限提示。
  • 为任何离线关键型 PWA(离线草稿、排队的变更、缓存媒体)调用 persist()
  • 在有意义的操作或安装之后申请持久化——而非首屏绘制时——以便存在参与度信号。
  • 始终处理 persist() 返回 false 的情况并优雅降级;不要因它而阻塞 UI。
  • 大量写入前调用 estimate();若 usage 接近 quota,先清理旧缓存。
  • 为 IndexedDB 与 Cache 写入包裹捕获 QuotaExceededError,随后逐出陈旧数据并重试。
  • 永远不要把配额当作固定值——用重新 estimate() 取代硬编码的容量预算。
  • 使用前特性检测 navigator.storage;较旧浏览器缺少 StorageManager API。

持久化存储决定了一个已安装的 PWA 是能可靠地离线打开,还是会在磁盘吃紧时悄无声息地丢失 用户排队的数据。仅在真实参与之后再申请,可让授予更可能、提示不扰人;而优雅处理 QuotaExceededError 则意味着磁盘满载时体验降级而非数据损坏——这正是用户对自己主动安装 的东西所期待的持久性。