使用 IndexedDB 持久化应用状态的最佳实践

了解如何在 IndexedDB 和常用状态管理库之间同步应用状态的最佳实践。

当用户首次加载网站或应用时,构建用于呈现界面的初始应用状态通常需要完成大量工作。例如,有时应用需要在客户端对用户进行身份验证,然后发出多个 API 请求,才能获得在页面上显示所需的所有数据。

将应用状态存储在 IndexedDB 中是加快重复访问加载时间的绝佳方式。然后,应用可以与后台的任何 API 服务同步,并采用“在重新验证时使用过时数据”策略,以便使用新数据延迟更新界面。

不过,使用 IndexedDB 时,有许多重要事项需要考虑,这些事项对于刚开始接触这些 API 的开发者来说可能并不明显。本文档解答了常见问题,并讨论了在 IndexedDB 中保留应用状态时需要注意的一些最重要的事项。

确保应用的行为可预测

IndexedDB 的许多复杂性源于以下事实:您(开发者)无法控制的因素太多了。本部分将探讨在使用 IndexedDB 时必须注意的许多问题。

写入存储空间的操作可能会失败

写入 IndexedDB 时发生错误的原因有很多种,在某些情况下,这些原因超出了开发者可控范围。例如,某些浏览器不允许在无痕浏览模式下写入 IndexedDB。此外,用户使用的设备也有可能磁盘空间几乎用尽,浏览器会限制您存储任何内容。

因此,请务必始终在 IndexedDB 代码中实现适当的错误处理。这也意味着,通常最好将应用状态保留在内存中(除了存储之外),以便在以无痕式浏览模式运行或存储空间不可用时(即使某些需要存储空间的其他应用功能无法运行),界面也不会中断。

存储的数据可能已被用户修改或删除

与服务器端数据库不同,您无法限制对客户端数据库的未经授权的访问,浏览器扩展程序和开发者工具可以访问客户端数据库,并且用户可以清除这些数据库。

虽然用户修改本地存储的数据的情况可能并不常见,但清除本地存储的数据的情况却很常见。请务必确保您的应用能够处理这两种情况,且不会出错。

存储的数据可能已过时

与上一部分类似,即使用户未自行修改数据,他们在存储空间中的数据也可能是由较低版本的代码(可能存在 bug)写入的。

IndexedDB 内置支持使用其 IDBOpenDBRequest.onupgradeneeded() 方法升级架构版本;不过,您仍然需要编写升级代码,使其能够处理来自旧版(包括存在 bug 的版本)的用户。

单元测试在此非常有用,因为通常无法手动测试所有可能的升级路径和用例。

确保应用的性能稳定

IndexedDB 的一项关键功能是其异步 API,但请不要被这点误导,以为在使用它时无需担心性能。在许多情况下,不当使用仍会阻塞主线程,从而导致无响应。

一般来说,对 IndexedDB 的读写操作不应超过对所访问数据所需的操作。

虽然 IndexedDB 允许将大型嵌套对象存储为单个记录(从开发者的角度来看,这样做确实非常方便),但应避免这种做法。原因在于,IndexedDB 存储对象时,必须先创建该对象的结构化克隆,而结构化克隆过程会在主线程中进行。对象越大,阻塞时间就越长。

这在规划如何将应用状态持久化到 IndexedDB 时会带来一些挑战,因为大多数热门状态管理库(例如 Redux)都是通过将整个状态树作为单个 JavaScript 对象进行管理来工作的。

虽然以这种方式管理状态有很多好处,并且将整个状态树作为单个记录存储在 IndexedDB 中可能很诱人且方便,但在每次更改后执行此操作(即使进行了节流/debounce)都会导致主线程不必要地阻塞,从而增加写入错误的可能性,在某些情况下,甚至会导致浏览器标签页崩溃或无响应。

您应将整个状态树拆分为单独的记录,而不是将其存储在单个记录中,并且只更新实际发生更改的记录。

与大多数最佳实践一样,这并不是一个非此即彼的规则。如果无法拆分状态对象并仅写入最小更改集,则最好将数据拆分为子树并仅写入这些子树,而不是始终写入整个状态树。小幅改进总比完全不改进要好。

最后,您应始终衡量您编写的代码对性能的影响。虽然向 IndexedDB 执行小写入的性能确实优于执行大写入,但只有当应用向 IndexedDB 执行的写入实际上会导致长任务阻塞主线程并降低用户体验时,这一点才重要。衡量效果非常重要,这样您才能了解自己要优化什么。

总结

开发者可以使用 IndexedDB 等客户端存储机制来改进应用的用户体验,不仅可以跨会话保留状态,还可以缩短重复访问时加载初始状态所需的时间。

虽然正确使用 IndexedDB 可以显著改善用户体验,但如果使用不当或未能处理错误情况,可能会导致应用无法正常运行,并让用户感到不满。

由于客户端存储涉及许多超出您控制范围的因素,因此请务必对代码进行充分测试并妥善处理错误,即使是最初看起来不太可能发生的错误也是如此。