状态管理
企业应用程序通常需要持久化状态,并允许用户以各种方式查看和操作这些数据。当需要同时在多处访问和编辑同一数据,且要保持数据的一致性时,状态管理可以成为大型应用程序中最复杂的领域之一。
状态通常存在位于 web 应用程序组件外部的数据存储或数据库中,这意味着一些状态管理复杂性需要在应用程序之外解决。然而,对于数据在应用程序与其用户之间流动的情况,有几个范例能够大大的降低管理复杂状态的风险。
响应式的数据修改
以命令式方式编写的应用程序会描述应更改哪些数据,应该如何更改,以及指定必须在何时何地更改。如果通过某种形式的计算或赋值在逻辑上连接多块数据,则这些连接在一段时间后只能表示成离散的点。久而久之,除了这些点之外,可能会以违反预期逻辑连接的方式修改任何数据值。
相反,以响应式方式编写的应用程序设法提升数据之间的逻辑连接,并放弃对明确地指定何时何地修改数据的控制,以使逻辑数据连接始终保持一致。
具有多个服务层的复杂应用程序可能会在多处描述相同的数据,因为它们散落在应用程序的各处——这方面的一种常见模式是使用数据传输对象。一段数据的描述位置越多,则维护应用程序状态完整性的复杂度呈指数级增长。
任何应用程序只要 UI 需要动态展示(包括 web 应用程序),都会遇到维护逻辑数据连接一致性的问题。这些应用程序中的数据通常至少有两种表示方式。
举例说明问题
假设有一个待办事项应用程序,它存储了一组任务,当向用户显示时,每个任务都有以下两种数据表示方式:
- 任务的确切描述(它的“真实来源”,例如它在数据存储中的值)
- 任务描述的副本,通过 UI 元素(如 label 或 textbox)呈现给用户。
如果用户只能查看任务,则有几个问题与如何修改任务的描述以让用户可见有关。
如果在底层的数据存储中更改了任务,则需要通过 UI 向上传播新的描述信息,这样用户就不会查看过时的数据。如果任务显示在 UI 的多个位置,则所有实例都需要更新,确保用户不会在不同位置看到的数据不一致。
如果用户还可以修改任务(比如更改描述信息),则还需要解决其他问题。
任务描述现在有两处真正的来源:数据存储中的旧值,以及用户在 textbox 中输入的新值。
然后,需要将修改请求传回给底层的数据存储,以便用新值替换旧值。修改完成后,需要将新的任务描述返回给用户,让用户看到更改后的正确值。尝试修改任务描述时会发生的任何错误也需要在数据交换时考虑。
Dojo 的状态管理
对于最基本的状态管理需求,部件可以使用本地变量管理自身的状态。虽然这种方法有助于隔离和封装,但它只适用于非常简单的用例,如只在应用程序中出现一次的部件,或者与应用程序处理的所有其他状态都断开了连接的部件。
随着在部件间共享状态的需求增加,Dojo 支持响应式的控制反转。将状态提升到父容器部件中,然后使用子部件的 properties 接口注入到子部件中。如果需要,这种状态提升可以横穿整个部件层级,将状态集中在应用程序根部件中,然后将部分状态注入到相关的子分支中。
对于更复杂的需求,或者对于较深的部件层级且不希望在不相关的中间层传递状态,则外部的数据存储可能是最好的方法。集中的数据存储能够帮助应用程序处理大量的状态,允许复杂的状态编辑操作,或者在多处请求相同的状态子集。
Dojo 提供了一个 Store 组件,它支持多种高级的状态管理需求,例如:
- 内置支持异步调用,例如调用远程服务进行数据管理。
- 状态操作按确定的顺序执行。
- 记录状态操作历史,允许回滚或撤销操作。
- 中间件用于包装数据操作流程,可添加横切点,如用于授权或记日志。
- 内置支持基于 LocalStorage 的数据存储,有助于实现 PWA。
- 支持乐观的数据更新,失败时会自动回滚。