网络存储
https://web.dev/storage-for-the-web/
在浏览器中存储数据有许多不同的选项。哪一个最适合你的需要?
Internet连接可以是松散的,也可以是不存在的,这就是为什么支持离线和可靠的性能是渐进式网络应用程序(progressive web apps,简称为 PWA)。即使在完美的无线环境中,明智地使用缓存和其他存储技术也可以大大改善用户体验。有几种方法可以缓存静态应用程序资源(HTML、JavaScript、CSS、图像等)和数据(用户数据、新闻文章等)。但是,哪一个是最好的解决办法呢?你能存多少数据?你如何防止它被删除?
该用什么?
以下是存储资源的一般建议:
- 对于加载应用程序和基于文件的内容所需的网络资源,请使用 Cache Storage API(它是 service workers 的一部分).
- 对于其他数据,请使用IndexedDB (使用 promise 包装器).
每个现代浏览器都支持IndexedDB和Cache Storage API。它们都是异步的,不会阻塞主线程。它们可以从window
对象、web workers 和 service workers进行访问,使代码中的任何位置都可以轻松地使用它们。
其他存储机制呢?
浏览器中还有其他几种存储机制,但它们的使用有限,可能会造成严重的性能问题。
SessionStorage 是特定于选项卡的(tab specific),并限定为该选项卡的生存期。它可能有助于存储少量的会话特定的信息,例如IndexedDB 的密钥。我们应该谨慎地使用它,因为它是同步的,并且会阻塞主线程。它仅限于5MB,只能包含字符串。因为它是特定于选项卡的,所以web workers 或service workers 无法访问它。
LocalStorage 应该避免使用,因为它是同步的,并且会阻塞主线程。它仅限于5MB,只能包含字符串。web workers 或service workers 无法访问LocalStorage。
Cookies 有其用途,但不应用于储存。Cookie是与每个HTTP请求一起发送的,因此存储超过“少量数据”的任何内容都会显著增加每个网络请求的大小。它们是同步的,web workers 无法访问它们。与LocalStorage 和SessionStorage 一样,cookies 仅限于字符串。
File System API 和 File Writer API 提供了将文件读写到沙箱文件系统的方法。虽然它是异步的,但不推荐它,因为它是只适用于基于Chromium 的浏览器.
File System Access API 目的是使用户能够轻松地读取和编辑本地文件系统上的文件。在页面可以读取或写入任何本地文件之前,用户必须授予权限,并且授权不会在会话之间保持。
WebSQL 应该不再使用,且应该将现有的使用迁移到IndexedDB。几乎所有的主流浏览器都已移除对其的支持。W3C在2010年停止了维护WebSQL规范,并且没有计划进一步更新。
Application Cache 应该不再使用,且应该将现有的使用迁移到service worker 和Cache API。它已被弃用,并且对它的支持将来也会从浏览器中被删除。
我能存多少数据?
总之,很多,至少有一两百兆字节,而且有可能达到好几百兆字节或更多。浏览器实现各不相同,但可用的存储量通常是基于设备上可用的存储量。
- Chrome 允许浏览器占用整个磁盘空间的80%。一个源(origin)最多可以占用整个磁盘空间的60%。您可以使用StorageManager API 来确定最大可用配额。其他基于Chromium的浏览器可能允许浏览器使用更多的存储空间。参见PR #3896 有关Chrome实现的详细信息。
- Internet Explorer 10及更高版本可以存储多达250 MB,并且在使用超过10 MB时将提示用户。
- Firefox 允许浏览器使用高达50%的空闲磁盘空间。eTLD+1组(例如,example.com,www.example.com 和foo.bar.example.com)最多可使用2GB。您可以使用StorageManager API 来确定还有多少可用空间。
- Safari (包括桌面和移动版)似乎允许大约1GB。当达到限制时,Safari将提示用户,以200 MB的增量增加限制。我找不到这方面的任何官方文档。
- 如果PWA被添加到Mobile Safari的主屏幕上,它似乎创建了一个新的存储容器,PWA和Mobile Safari之间没有任何共享。一旦一个已安装的PWA达到配额,似乎没有任何方法来请求额外的存储。
过去,如果站点超过了存储数据的某个阈值,浏览器将提示用户授予使用更多数据的权限。例如,如果原点使用超过50 MB,浏览器将提示用户允许它存储最多100 MB,然后以50 MB增量再次询问。
今天,大多数现代浏览器将不会提示用户,并将允许网站使用其分配的配额。Safari是个例外情况,它在750 MB处提示,请求存储最多1.1GB的权限。如果一个源试图使用超出其分配的配额数量,则进一步写入数据的尝试将失败。
如何检查有多少可用的存储空间?
在许多浏览器中,您可以使用StorageManager API 来确定某个源可用的存储量,以及已使用量。这个接口提供了IndexedDB 和Cache API 所使用的字节总数,并使计算大约剩余的可用存储空间成为可能。
if (navigator.storage && navigator.storage.estimate) {const quota = await navigator.storage.estimate();// quota.usage -> Number of bytes used.// quota.quota -> Maximum number of bytes available.const percentageUsed = (quota.usage / quota.quota) * 100;console.log(`You've used ${percentageUsed}% of the available storage.`);const remaining = quota.quota - quota.usage;console.log(`You can write up to ${remaining} more bytes.`);
}
StorageManager 不是在所有浏览器中都实现了的,您必须在使用之前检测它。即使在可用的情况下,仍然必须捕获(catch)超出配额的错误(见下文)。在某些情况下,可用配额有可能超过实际可用的存储量。
其他基于Chromium 的浏览器在报告可用配额时可能会考虑到可用空间的数量。Chrome浏览器不同,它将始终报告60%的实际磁盘大小。这有助于减少确定存储的跨源资源大小的能力。
检查 (Inspect)
在开发期间,您可以使用浏览器的开发工具 (DevTools )来检查不同的存储类型,并轻松清除所有已存储的数据。
Chrome 88中增加了一个新功能,允许您在Storage Pane 中手动控制站点的存储配额。此功能使您能够模拟不同的设备,并在低磁盘可用性的情况下测试应用程序的行为。在开发工具页面 点击 应用(Application) => 存储(Storage),启用模拟自定义存储配额 (Simulate custom storage quota)复选框,并输入任何有效数字以模拟存储配额。
在写这篇文章的时候,我(原作者)写了一个简单工具试图尽快使用尽可能多的存储空间。这是一种快速而简单的方法来实验不同的存储机制,并看看当使用所有了配额时会发生什么。
如何处理超配额?
当你超过配额时,你应该做什么?最重要的是,您应该始终捕获并处理写错误,无论它是 QuotaExceededError 或者别的什么。然后,根据您的应用程序设计,决定如何处理它。例如,删除很长时间没有被访问的内容,根据大小删除数据,或者让用户来选择要删除的内容。
当你超过了可用的配额时,IndexedDB 和Cache API 都会抛出一个 名为QuotaExceededError 的
DOMError 。
IndexedDB
如果一个源已经超出了其配额,则尝试写入IndexedDB将失败。将调用该事务(transaction)的 onabort() 处理程序,传递一个事件。该事件在错误属性中将包括一个DOMException。检查该错误的name将返回QuotaExceededError。
const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {const error = event.target.error; // DOMExceptionif (error.name == 'QuotaExceededError') {// Fallback code goes here}
};
Cache API
与IndexedDB 类似,如果一个源已经超出了其配额,则尝试写入Cache API的操作将拒绝,并显示QuotaExceededError DOMException。
try {const cache = await caches.open('my-cache');await cache.add(new Request('/sample1.jpg'));
} catch (err) {if (error.name === 'QuotaExceededError') {// Fallback code goes here}
}
清除回收工作如何进行?
网络存储分为两类:“尽力而为型(Best Effort)”和“持久型(Persistent)”。“尽力而为”意味着可以在不中断用户的情况下通过浏览器清除存储,但是对于长期或重要数据而言,持久性较差。 但当存储空间不足时,“持久型”存储空间不会被自动清除。 用户需要手动清除此存储(通过浏览器设置)。
默认情况下,网站的数据(包括IndexedDB,Cache API 等)属于“尽力而为型”,这意味着除非网站已请求持久存储,否则浏览器可能会自行决定将网站数据清除回收,例如,当设备存储空间不足时 。
“尽力而为型”的清除回收政策是:
- 当基于Chromium的浏览器空间不足时,它将开始删除数据,首先从最近使用最少的源中清除所有站点数据,然后再清除下一个,直到浏览器不再超出限制。
- Internet Explorer 10+不会删除数据,但将阻止源写入。
- 当可用磁盘空间被填满时,Firefox 将开始删除数据,首先清除最近使用最少的源中的所有站点数据,然后再清除第二个,直到浏览器不再超过限制。
- Safari 以前不删除数据,但最近对所有可写存储实施了新的七天上限(见下文)。
从iOS和iPadOS 13.4以及MacOS上的Safari 13.1开始,所有脚本可写存储都有7天的上限,包括IndexedDB、service worker 注册和Cache API。这意味着,如果用户不与该站点交互,Safari将在使用的7天后将所有内容从缓存中删除。这种清除政策不适用于已添加到主屏幕上的已安装PWA 。有关的完整详细信息,请参阅完全的第三方Cookie阻塞及更多。
你可以为你的站点请求持久存储,以保护关键的用户或应用程序数据。
奖励:为什么对IndexedDB使用包装器
IndexedDB 是一个低级别的API,在使用前需要进行大量的设置,这对于存储简单的数据特别痛苦。与大多数现代的基于Promise的API不同,它是基于事件的。诸如idb之类Promise 包装器,隐藏了IndexedDB 的一些强大的功能,但更重要的是,隐藏了IndexedDB 库附带的复杂机制(例如事务、架构版本控制)。
结论
有限的存储和提示用户存储越来越多的数据的时代已经一去不复返了。站点可以有效地存储运行所需的所有资源和数据。使用StorageManager API 您可以确定有多少可用空间,以及您已经使用了多少空间。有了持久存储,除非用户删除它,否则您可以保护它不被清除。
额外资源
- IndexedDB 最佳实践
- Chrome 网络存储和配额概念
致谢
略(请参见原文)