localStorage 有什么问题?
localStorage 在 2009年 作为一个5MB字符串存储出现。由于现在我们的注意力很容易分散,我们直接用几个要点来剖析:
- 字符串集合:它只能存储字符串。存储或检索时你必须进行序列化和反序列化。如果你忘了这一步,可能会遇到导致各种网站故障的怪异行为。例如,当你存储“true”或“false”时,还可能会遇到
null
、undefined
或“”这些潜在问题。 - 无结构化数据:JavaScript 有一个叫做结构化克隆算法的东西。你真的需要知道这是一个功能。现代的 API 和更新的 API,如 postMessage、WebWorkers、IndexedDB、Caches API、BroadcastChannel、Channel Messaging API、MessagePort 和 History API 都使用结构化数据。它解决了应用程序内序列化和反序列化 JSON 的问题。localStorage 没有更新这个功能,而且未来也没有讨论这方面的计划。
- 常见的安全风险:明确一点,你永远不应该将敏感数据存储在任何未专门为此设计的持久存储中。开发人员仍然常常将会话 ID、JWT、信用卡信息和 API 密钥存储在 localStorage 中。这是一个非常常见的安全漏洞,因为它和
window.localStorage
一样简单。 - 性能问题:localStorage 历史上确实有过性能不佳的时刻,尽管随着时间的推移进行了一些优化,性能有所提升。尽管如此,对于需要频繁事务处理的并发应用程序来说,它的性能仍然不够理想。如果你在最新的 MacBook 上进行基准测试,而没有限制性能,你可能不会理解低功耗设备上的成本影响。
- 大小限制:localStorage 有一个5MB 的容量上限,而且可能会被浏览器清理。对于现代应用程序来说,这是一个非常小的容量。很难存储这么少量的编码媒体。而且这不是固定的,就像所有的网络持久存储一样,localStorage 也可能会被浏览器在某个时刻清理。你需要管理这部分数据生命周期,尽管没有任何提醒。
- 无法在 Web Worker 中访问:我希望你能理解,localStorage 不是为未来或并发性而设计的 API,后者在低功耗设备上表现出色。
- 没有原子性或隔离性:localStorage 不能保证多个操作的原子性。没有锁机制来确保写入的内容,或防止信息被覆盖。
- 没有数据分离或精细的源范围限制:localStorage 只是一个字符串对象,没有数据分离,用户数据和应用数据混杂在一起。尽管它使用相同源策略,但没有办法限制特定域或子域的数据访问。这在满足 GDPR 标准时可能会导致重复的工作。
- 没有分组事务:localStorage 没有数据库定义的事务,但它也没有办法组合操作。每个操作都是同步的、非隔离的,没有锁机制。
- 序列化陷阱:由于管理不善,许多网站的状态出现了故障。从本文发表的时间算起,就在去年,作为顾客的我被3个不同的网络服务告知“清除你的缓存”,我发现这是由于不当的 localStorage 管理所致。如果你不熟悉 localStorage 存储数据时的陷阱,你可能会在应用程序中创建你永远不会遇到的错误。这些类型错误并不总是很明显,尤其是对新开发人员来说。
- 同步阻塞操作:localStorage 不是异步的,会阻塞主线程。它甚至可能使你的动画变得卡顿,这取决于你读写的频率和数据量。异步性是创建流畅应用程序的基础,尤其是移动设备。
WebSQL 去哪了?
WebSQL 旨在为网络提供一个简单的 SQL 数据库接口。它曾得到一些支持,但最终由于各种挑战而被弃用。
为什么他们放弃了这个项目?
- 单一供应商实现:WebSQL 主要是 webkit 的东西(最初是 Chrome 和 Safari)。其他主要浏览器供应商(Mozilla 和 Microsoft)缺乏支持,这使得开发人员在商业世界中几乎不采用。
- 没有 W3C 标准化:这对于采用至关重要。W3 在 2010 年似乎放弃了提案。
- 与 IndexedDB 竞争:在 WebSQL 崛起期间,IndexedDB 获得了更多的支持和关注。与 WebSQL 不同,IndexedDB 被设计为一个标准的、跨浏览器的解决方案。
- 安全问题:一些开发人员和安全专家对 WebSQL 的安全性表示担忧。他们对各种方面持怀疑态度,包括缺乏权限控制和 SQL 风格的漏洞。
最终,IndexedDB 成为了客户端存储的“推荐”标准,被视为更健壮和跨浏览器友好。但是,当大多数经验丰富的前端开发人员在本文发表时还在避免使用它时,“推荐”又有什么好处呢?
尽管存在缺陷,但在当时,WebSQL 在网络社区中备受赞誉,是一个值得竞争的对手。
那么 Cookies 呢?
Cookies 由 Netscape Communications 的网络浏览器程序员 Lou Montulli 在 1994 年创造。
当 cookies 开始搅局时,你们中的一些人甚至还没有出生。这篇文章的标题应该是“停止使用 Cookies 和 localStorage”,但那是一场非常艰难的战斗,(是的,我们应该使用安全的 cookies)
- 大小限制:Cookies 通常限制在每个域大约 4KB。
- 每次请求都发送数据:Cookies 会随每个 HTTP 请求发送到关联域。如果你的数据不需要每次请求都传输,这可能导致不必要的开销和增加的带宽使用。
- 安全问题:Cookies 更容易受到 XSS 的攻击。因为 cookies 自动包含在对域的每个请求中,它们可能成为恶意脚本的目标。
- 到期和生命周期:Cookies 被设计为到给定日期后过期。
- 增加延迟:因为 cookies 自动包含在每个 HTTP 请求中,它们通常会导致增加的延迟,从而使网站加载速度变慢。
为什么选择 IndexedDB
- 更好的性能:与 localStorage 不同,IndexedDB 是异步操作的,避免了阻塞操作。(API 是基于事件的,而不是基于 Promise 的)
- 充足的存储配额:IndexedDB 提供了比localStorage 的 5MB 上限更大的存储配额 (取决于浏览器、操作系统和可用存储)。
- 可靠性和结构化数据:在 localStorage 中存储和检索数据可能会产生不可预测的结果,如果操作不当的话。IndexedDB 采用了结构化克隆算法,确保了数据完整性。
但是…… 你可能不想直接使用 IndexedDB
本文的第二点 IndexedDB 是避免依赖的例外。可以将 IndexedDB 视为后端数据库,你可能需要一个 ORM 或数据库库来处理查询。这类似于需要一个 IndexedDB 库,因为 IndexedDB API 非常糟糕。
你想使用库的原因是因为它们通常:
- 基于 Promise
- 更容易使用
- 减少样板代码
- 专注于更有意义的事情。
我个人不建议使用大型 IndexedDB 库,因为你想要完成的事情不需要超过几个单数字 kB。过度的 IndexedDB 库对于你的实际场景没有任何有意义的帮助。
我发现大多数 IndexedDB 库的一个问题是,它们主要围绕版本控制,这是你可能完全不需要的,特别是如果你只是在寻找一个合理的 localStorage 替代品。 我推荐 localForage,这是一个全面的库,它提供了和localStorage一样的api并支持IndexedDB, WebSQL, localStorage三种存储方式。
结论
尽管并非总是可能,但在当今时代,我们应该尽量避免使用 localStorage。
新开发人员在使用 Promise()、async/ await 和结构化数据方面有更有意义和可复用的知识要学习,而不是试图弄清楚为什么数字 '0' 会使他们的条件成为 truthy 或为什么他们的愤怒用户收到了 null
的交付。
总结一下,这项技术有速度优势,你可以可靠地存储类型,甚至可以使用 游标 遍历条目。有了这项技术,你可以构建一个客户端搜索引擎,通过累积数据获取,而不会让你的动画像 localStorage 阻塞和干扰那样变得紧张 👀。
IndexedDB 通常被描述为“低级”。IndexedDB 绝对不是低级的,它只是一个具有老式且不友好的语法的 API。但这并不否认它的底层能力,因此常见的库使用。
你不一定需要直接使用 API 除非你想要,但使用一个小型的封装库,或者为什么不自己制作一个呢?