Skip to content

浏览器中的存储API:LocalStorage、IndexedDB、Cookies、OPFS 与 WASM-SQLite

在构建现代Web应用程序时,常常需要在客户端浏览器保存数据。以前我们只需要在浏览器中保存一些标记,现在浏览器性能发展。许多网站往单页APP方向发展,前端需要处理大量和复杂的数据。有时需要在离线完成任务。浏览器为了适应这些需求,提供了不同的API支持现代网页。从早期的 Cookies,LocalStorage到新兴的 IndexedDB,OPFS,每种技术都有其独特的优势和适用场景。这篇Blog整理了这几种存储技术的特点,方便技术选型。

浏览器中存储API概览

Cookies

Cookies是最早的浏览器存储技术之一。它主要用于会话管理、个性化和跟踪,存储的数据量较小(约4KB),并且会随每个HTTP请求发送到服务器。尽管Cookies的存储容量有限,但其访问性能非常出色,尤其是在会话管理和跨子域共享数据方面。

会话管理:JSP 初始化连接时,会初始化一个session对象,并且把sessionid存入Cookies。JSP中setAttribute方法会把值存到该连接的session对象中。

跨子域共享数据:如果两个网站在同一个一级域名下,可以设置Cookies的SameSite属性实现共享。

LocalStorage

LocalStorage是WebStorage规范的一部分。它提供了一个简单的键值对存储API,适合存储需要在会话之间持久化的小量数据。LocalStorage的存储上限通常为5MB,但其操作是同步的,可能会阻塞JavaScript主线程。处理大量数据或复杂操作,可能会造成页面明细卡顿。

IndexedDB

IndexedDB于2015年引入,是一个用于存储大量结构化数据的低级API。它支持异步操作和索引查询,但缺乏复杂的查询功能。IndexedDB的存储容量取决于设备的磁盘空间,通常可以存储千兆字节的数据。IndexedDB 3.0的推出后,基于Promise的异步调用使其在开发中更加实用。我们一般不会直接使用IndexedDB,通常通过引入包,间接调用IndexedDB的接口。

OPFS(源私有文件系统)

OPFS是一个相对较新的API,是为了IO密集型应用设计的。支持异步和同步操作,适合处理二进制数据或大文件。OPFS的存储容量与IndexedDB相同,都取决于设备的可用磁盘空间。能够高效地写入和读取文件。确保在处理图像和视频等大型文件时保持程序流畅。

WASM-SQLite

WebAssembly(Wasm)是一种二进制格式,允许在浏览器中运行高性能代码。通过将SQLite编译为Wasm,开发者可以在浏览器中使用SQLite作为数据库。WASM-SQLite的初始化时间较长,但其查询性能出色,语法不用重复学习。

WebSQL

WebSQL 基于 SQLite,可以在浏览器使用近似SQL的语法处理数据。近年来,WebSQL 已从浏览器中移除,原因有很多:

  • WebSQL 没有标准化,并且基于 SQLite 源代码的单一特定实现的 API 很难成为标准。
  • WebSQL 要求浏览器使用特定版本的 SQLite(版本 3.6.19),这意味着每当 SQLite 有任何更新或错误修复时,都无法将其添加到 WebSQL 中,否则可能会破坏网页。
  • 像 Firefox 这样的主流浏览器从未支持过 WebSQL。

因此,在接下来的内容中,我们将忽略 WebSQL。

功能对比

存储复杂JSON文档

  • IndexedDB:原生支持JSON对象。
  • WASM-SQLite:支持在text列中存储JSON,并支持深度查询。
  • 其他API:只能存储字符串或二进制数据,需通过JSON.stringify()额外转换。

多标签页支持

  • LocalStorage:通过storage事件支持跨标签页数据同步。
  • IndexedDB:需通过BroadcastChannelSharedWorker实现跨标签页数据同步。

WebWorker支持

  • LocalStorage和Cookies:这两个建议用于少量数据,无法在WebWorker中使用。
  • 其他API:支持在WebWorker中使用,OPFS的createSyncAccessHandle()方法只能在WebWorker中使用。通常在数据体积较大时使用WebWorker。

存储大小限制详解

  • Cookies:根据 RFC-6265,数据量限制约为 4KB。由于 Cookies 会随每个 HTTP 请求发送到服务器,过大的 Cookie 数据可能会导致请求头部过大,从而引发服务器拒绝请求的问题。
  • LocalStorage:不同浏览器的存储大小限制有所差异,一般在 4MB 到 10MB 之间。例如,Chrome/Chromium/Edge 通常为每个域名提供 5MB 的存储空间,Firefox 为每个域名提供 10MB 的存储空间,Safari 则在 4 - 5MB 之间(不同版本略有差异)。
  • IndexedDB 和 OPFS:这两种存储方式的最大存储大小通常取决于浏览器的实现和用户设备上的可用磁盘空间。在 Chromium 浏览器中,IndexedDB 可以使用高达 80% 的总磁盘空间,而 OPFS 与 IndexedDB 具有相似的存储大小限制,开发者可以通过调用 await navigator.storage.estimate() 来获取存储大小限制的估计值。

性能总结与建议

  • LocalStorage:具有极快的读写速度,尤其适用于存储少量且频繁访问的数据。然而,由于其同步 API 的特性,在处理大量数据操作时可能会阻塞主线程,因此不建议用于大型批量操作。同时,其键值对的存储方式在处理复杂数据查询时效率较低,当需要进行基于索引的范围查询时,应考虑其他更合适的存储方式。
  • OPFS:在使用 createSyncAccessHandle() 方法并结合 WebWorker 时,能够显著提高写入和读取性能,特别适用于处理数据密集型的应用程序,如大型文件存储和二进制数据处理。但在处理每个文档一个文件的写入方式时,性能相对较低,开发者可以考虑将数据存储在一个文件中,以提高性能,就像 RxDB OPFS RxStorage 所做的那样。
  • SQLite WASM:虽然在初始化时需要下载和解析较大的二进制文件,导致启动时间较长,但在处理复杂查询和批量操作时表现出较高的性能。对于那些只启动一次并长时间运行的应用程序来说,这可能是一个不错的选择。然而,对于在多个浏览器标签中频繁打开和关闭的应用程序,初始化时间可能会成为一个问题。

优化与未来展望

在实际应用中,开发者可以通过多种方式进一步优化存储操作的性能:

  • IndexedDB:可以采用分片策略,将数据分布在多个数据库或 Web Workers 中,以减轻单个数据库的压力,提高查询性能。同时,自定义索引策略也可以根据具体的数据访问模式,进一步优化查询效率。
  • OPFS:如前所述,将数据存储在一个文件中而不是每个文档一个文件,可以显著提高性能。此外,结合其他技术,如数据压缩和缓存机制,可以进一步提升 OPFS 的存储和读取效率。
  • 混合使用多种技术:例如,在 RxDB 中,通过将初始元数据存储在 LocalStorage 中,而将 “普通” 文档存储在 IndexedDB 中,可以在提高初始启动时间的同时,确保数据的高效查询和存储。

展望未来,随着浏览器技术的不断发展,我们有理由期待更多的性能优化和新功能的出现:

  • WebAssembly 与持久存储的融合:如果未来能够直接从 WebAssembly 进程中访问持久存储,那么在浏览器中运行 SQLite(或类似的数据库)可能会成为最佳的存储解决方案,将进一步提升数据操作的性能和灵活性。
  • 主线程与 WebWorker 通信优化:目前在主线程和 WebWorker 之间发送数据的速度较慢,未来有望通过技术改进,减少数据传输的开销,提高数据处理的效率。
  • IndexedDB 的持续改进:IndexedDB 最近获得了对存储桶的支持(仅限 Chrome),这可能会为其性能提升带来新的机遇。随着浏览器厂商对 IndexedDB 的不断优化,它有望在未来的 Web 开发中发挥更加重要的作用。

总之,在选择浏览器存储 API 时,开发者需要综合考虑应用程序的需求、数据量、读写频率以及性能要求等因素,权衡各种存储方式的优缺点,以选择最适合的存储解决方案。