利用 indexedDB 保持应用状态的最佳实践

本文翻译自 developers.google.com

作者:Philip Walton

原文链接:Best Practices for Persisting Application State with IndexedDB

当网站或应用首次被加载时,首先通常需要准备大量工作去构造初始的应用状态信息,然后再使用这些信息去渲染界面。例如,有的应用需要用户进行身份验证,之后才能去发一些 API 请求获取数据,最后才是将返回的数据呈现到页面中。

可见,如果把应用状态信息存储到 indexedDB 中,将能够提升一些频繁访问页面的加载时间。通过 indexedDB 还能够采用失效更新策略(stale-while- revalidate strategy),在后台与任何 API 服务进行同步,只有当数据更新时才会去更新 UI。

然而在使用 indexedDB 时,仍然有很多重要的事情需要考虑,开发者对这些新的 API 也不一定非常熟悉。因此这篇文章将回答一些 indexedDB 的常见问题,并且讨论在使用 indexedDB 保持应用状态时需要关注的一些重点。

info

Note:

这篇文章的关注点在于如何保持应用状态数据,比如那些用来渲染用户的界面的数据等等。在存储其他类型的数据的时候,这篇文章给出的建议并不是必要的。

保持你的程序功能的可预测性

造成 indexedDB 复杂的原因在于,应用有很多方面开发者无法完全控制。本章节将探讨在使用 indexedDB 时必须牢记的许多问题。

写入存储可能会失败

造成 indexedDB 写入失败的原因有很多,在某些情况下这些原因不能为开发者所控制。例如,一些浏览器目前不允许在隐私浏览模式下写入 indexedDB。也有可能是用户使用的设备磁盘快满了,因此浏览器会限制您存储的任何东西。

因此,在操作 indexedDB 的代码中执行错误处理操作变得至关重要。这也意味着将应用程序状态保存在内存(除了存储它)中是个好主意,这样在各种存储失败的情况下,UI不会被中断。

info

Note:

新的存储 API 目前正在开发当中,这些 API 将能够让开发者在请求大容量存取配额或者是持久性存储的时候,获取可用的存储空间的估计。这就意味着即使在执行标准清除缓存/cookie 的操作时,用户也可以选择保留某些站点的数据。

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

不像服务端数据库那样开发者可以限制未经授权的请求,客户端数据库可以被浏览器扩展插件和开发者工具所访问,并且用户可以清除它们。

虽然用户修改本地存储的数据并不是很常见,但用户清除它们却是很常见的。因此应用程序需要控制好这类可能出现的情况以确保不会发生错误。

存储的数据可能会过期

与上一节类似,即使用户本身没有修改过数据,存储的数据也可能是由旧版本的代码编写的,而这些旧版本的代码中可能包含错误,因此存储的数据还可能是由包含 bug 的代码写出的带有问题的数据。

indexedDB 内置支持了模式版本(schema version),并通过 IDBOpenDBRequest.onupgradeneeded() 方法进行升级;但是,你仍然需要编写升级策略的代码,以便可以处理来自以前版本的用户(包括具有错误的版本)。

单元测试在这里非常有用,因为手动测试所有可能的升级路径和安利通常是不可行的。

保持你的程序性能

indexedED 的主要功能之一就是它的异步 API,但是这不意味着你不需要关心使用它时的性能。在一些情况下,不正确的使用仍然可以阻止主线程,可能会造成应用的崩溃和无响应。

作为一般规则,indexedDB 的读写不应该大于待访问数据的大小。

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

这就给规划如何将应用状态保留到 indexedDB 带来一些挑战。因为大多数流行的状态管理库(如 Redux)会通过整个状态数作为单个 Object 进行管理。

虽然这种方式管理状态有很多好处(例如它将使您的代码易于推理和调试),同时将整个状态树作为单一记录存储到 indexedDB 中可能会非常诱人和方便,但是在每次改动之后(即使使用了 throttled/debounced)将导致主线程不必要的阻塞,这将增加写入错误的可能性,并且在某些情况下甚至会导致浏览器选项卡崩溃或无响应。

所以应该将整个状态树分解为单个记录进行存储,并且只更新实际更改的记录,而不是将整个状态树都存储到单个记录中。

与大多数最佳实践一样,这并不是一个要么全有要么全无的规则(all-or-nothing rule)。在分解状态对象并且写入最小变更集不可行的情况下,将数据分解为子树,只写入这些数据仍然优于写入整个状态树。一点点改进比没有改进更好。

最后,你应该经常测试你编写代码的性能。尽管对于 indexedDB 的少量写入比大量写入性能要更好,但应该只有当 indexedDB 的操作是导致主线程阻塞并降低用户体验时的主要原因时,才需要着手去做相关性能优化。只有通过测试,才能够让你了解代码真正需要优化的部分。

小结

开发人员可以利用像 indexedDB 这样的客户端存储机制,不仅能够在会话中持久化状态,还可以减少在重复访问时加载初始状态所需要的时间,从而改善用户的应用体验。

需要注意的是,尽管正确使用 indexedDB 可以显著提高用户体验,但是在使用错误或者无法处理错误的情况下可能会导致应用损坏并且影响用户体验。

由于客户端存储涉及许多不受控制的因素,因此您的代码需要经过全面的测试并且能处理各种错误,即使这些错误似乎不太可能发生。