存储 · 概念
逐出与尽力而为存储
发布于
在浏览器授予持久化之前,每个源的存储桶都是尽力而为(best-effort)模式。磁盘不足、浏览器总上限被触及,或者在 Safari 里用户连续七个浏览器使用天数没有与站点交互时,尽力而为的存储桶会被整体删除。删除的单位是存储桶,所以被逐出的源会同时失去缓存、数据库和 Service Worker 注册。
Storage 标准定义了 best-effort 和 persistent 两种桶模式,并要求存储桶一旦被清除就必须整体清除(Storage Standard,whatwg.org)。触发条件和顺序由浏览器决定,各引擎在这里分道。
Chromium 与 Firefox 的压力驱动逐出
Section titled “Chromium 与 Firefox 的压力驱动逐出”Chromium 在总上限(磁盘总容量的 80%)被超过时开始逐出,Firefox 在磁盘被填满时开始。两者都按最近最少使用的顺序:最久未访问的尽力而为源先被清除,然后是下一个,直到用量回到上限以下(Storage quotas and eviction criteria,developer.mozilla.org)。因此一个源远低于自身配额时也可能被逐出:触发条件是所有源的合计用量,选择依据是访问时间而不是体积。持久化存储桶会被跳过。
Safari 的七天规则
Section titled “Safari 的七天规则”WebKit 有三个逐出触发条件:超过总配额(浏览器应用为磁盘的 80%,其他内嵌网页内容的应用为 20%)、系统存储压力,以及智能跟踪防护(ITP)(Updates to Storage Policy,webkit.org)。ITP 触发自 iOS 13.4 和 Safari 13.1 起生效:用户连续七个 Safari 使用天数没有与站点交互,该源由脚本写入的存储即被删除。计时按「使用了 Safari 的天数」而非日历天数,覆盖 IndexedDB、localStorage、sessionStorage、媒体密钥以及 Service Worker 注册和缓存(Full Third-Party Cookie Blocking and More,webkit.org)。源有打开的页面或存储桶处于持久化模式时不会被逐出。
添加到主屏幕的 Web App 运行在 Safari 之外,有自己独立的使用天数计数,打开已安装的应用就会重置计时;Apple 表示不预期此类应用的第一方数据被删除。
隐私窗口是另一套生命周期。Chrome 的无痕模式把源配额降到约磁盘的 5%,并在最后一个无痕窗口关闭时丢弃数据(Storage for the web,web.dev);Firefox 和 Safari 同样在隐私会话结束时清除存储。这都不是 LRU 逐出,persist() 也不会改变它。
逐出不会在页面里留下任何事件,只有间接信号,而每一条都不需要特殊工具就能验证。
- 逐出之后,
navigator.storage.estimate()报告该源的usage接近 0 而quota不变,因为配额是磁盘比例,只是桶空了。 - 存储桶被清除的源上,
caches.keys()解析为[],indexedDB.databases()也解析为[](BCDapi.IDBFactory.databases:Chrome 72、Firefox 126、Safari 14)。Service Worker 可以据此发现自己在install时写入的预缓存已经不在,并在下一次activate时重建。 - Safari 的七天删除同时移除 Service Worker 注册,所以超期后的访问从
navigator.serviceWorker.controller === null开始,install事件重新执行。
下面的检查在页面加载时运行,借 localStorage 里的一个标记区分「被逐出」和「首次访问」:Safari 会在同一次清理中连它一起清掉,而 Chromium 和 Firefox 逐出受配额管理的存储桶时不会动它。
async function detectEviction() { if (!('caches' in self) || !('storage' in navigator)) { return 'unknown'; // 这里没有受配额管理的存储:无从检测 } const names = await caches.keys(); const seenBefore = localStorage.getItem('shell-installed') === '1'; if (names.length === 0 && seenBefore) { return 'evicted'; // 存储桶在压力下被清除;重建预缓存 } localStorage.setItem('shell-installed', '1'); return names.length === 0 ? 'first-visit' : 'intact';}得到 'evicted' 就该重新执行预缓存,并在应用已经赢得资格时调用 navigator.storage.persist(),让下一次清理跳过这个源。
- Storage Standard: buckets and eviction(whatwg.org)
- Updates to Storage Policy(webkit.org)
- Full Third-Party Cookie Blocking and More(webkit.org)
- StorageManager.persist() 与 persisted()
- StorageManager.estimate()
- CacheStorage 与 caches 全局对象
规范
| 规范 | 状态 |
|---|---|
| Persistent Storage(持久化存储) | W3C |
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Desktop) | 支持 | 55 | 高 | 来源 | — |
| Chrome (Android) | 支持 | 55 | 高 | 来源 | 1 |
| Edge (Desktop) | 支持 | 79 | 高 | 来源 | 2 |
| Firefox (Desktop) | 支持 | 57 | 高 | 来源 | — |
| Firefox (Android) | 支持 | 57 | 高 | 来源 | 3 |
| Safari (macOS) | 支持 | 15.2 | 高 | 来源 | — |
| Safari (iOS) | 支持 | 15.2 | 高 | 来源 | 4 |
| Samsung Internet | 支持 | 6.0 | 高 | 来源 | 5 |
| WebView (Android) | 支持 | 55 | 高 | 来源 | 6 |
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- 由 browser-compat-data 镜像自 Firefox 的数据推导。
- 由 browser-compat-data 镜像自 Safari 的数据推导。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。