我正在写一个有点"有趣"的Scala/Scala.js项目。
在我的服务器上,我有由uuid
-s 引用的实体 (在参考文献中)。
为了"好玩",我不想使用flux/redux架构,但仍然在客户端上使用React(使用ScalaJS-React)。
我试图做的是拥有一个简单的缓存,例如:
- 当 React
UserDisplayComponent
想要显示带有uuid=0003
的Entity
User
时 - 然后
render()
方法调用Cache
(作为prop
传入) - 假设这是
UserDisplayComponent
第一次要求这个特定的User
(带有uuid=0003
),而Cache
还没有它 - 然后
Cache
会创建一个AjaxCall
,从服务器获取User
- 当
AjaxCall
返回时,Cache
触发器re-render
- 但是!现在,当组件从
Cache
请求User
时,它会立即从Cache
获取User
Entity
,并且不会触发AjaxCall
我想实现的方式如下:
- 我开始
render()
render()
里面的"东西"要求Cache
提供各种Entities
Cache
返回Loading
或Entity
本身。- 在渲染结束时,
Cache
将所有AjaxRequest
-s 发送到服务器并等待所有 -s 返回 - 一旦所有
AjaxRequests
都返回了(为了简单起见,我们假设它们返回了),Cache
触发了一个"re-render()
",现在之前请求的所有实体都由Cache
立即提供。 - 当然,新到达的
Entity
-s 可能会触发render()
获取更多Entity
-s,例如,如果我加载一个例如case class UserList(ul: List[Ref[User]])
类型的Entity
。 但是我们现在不要担心这个。
问题:
1)如果我以这种方式进行状态处理,我真的做错了什么吗?
2)是否有已经存在的解决方案?
我环顾四周,但一切都是FLUX/REDUX等......沿着这些思路... - 我想避免这样做:
- "好玩"
- 好奇心
- 勘探
- 四处玩耍
- 我认为这个简单的缓存对于我的用例来说会更简单,因为我想以一种简单的方式将基于"REF"的"域模型"带到客户端:就好像客户端在服务器上一样,网络将是无限快和零延迟(这是缓存将模拟的)。
考虑构建丰富的动态 Web UI 需要解决哪些问题,以及哪些库/层通常会为您处理这些问题。
1. DOM 事件(点击等)需要触发状态更改
这相对容易。DOM 节点公开基于回调的侦听器 API,该 API 可以直接适应任何架构。
2. 状态更改需要触发对 DOM 节点的更新
这更棘手,因为它需要以可维护的方式高效完成。您不希望在状态更改时从头开始重新渲染整个组件,并且您也不想编写大量 jquery 风格的意大利面条代码来手动更新 DOM,因为即使在运行时效率高,这也太容易出错。
这个问题主要是为什么像 React 这样的库存在,他们将其抽象到虚拟 DOM 后面。但是你也可以在没有虚拟DOM的情况下将其抽象出来,就像我自己的层流库一样。
放弃解决此问题的库解决方案仅适用于更简单的应用程序。
3. 组件应该能够读/写全局状态
这是通量/冗余解决的部分。具体来说,这些是问题 #1 和 #2 再次出现,除了应用于全局状态而不是组件状态。
4. 缓存
缓存很难,因为缓存需要在某个时候失效,除了上面的其他所有内容之上。
助焊剂/冗余对此根本没有帮助。其中一个有帮助的库是 Relay,它的工作方式与您提出的解决方案非常相似,只是更加详细,并且位于 React 和 GraphQL 之上。阅读其文档将帮助您解决问题。如果你不需要整个 React/GraphQL 包袱,但你需要了解现有技术,你绝对可以在普通 Scala 中实现一小部分 Relay 的功能.js。
5. 序列化和类型安全
这是此列表中唯一与Scala相关的问题.js而不是一般的Javascript和SPA。
Scala 对象需要序列化才能通过网络传输。进入JSON,protobufs或其他任何东西,但你需要一个不涉及容易出错的手动工作的系统。有许多 Scala.js 库可以解决这个问题,例如 upickle、Autowire、endpoints、sloth 等。关键词:"Scala JSON 库"或">Scala 类型安全 RPC",具体取决于您想要哪种解决方案。
我希望这些原则足以作为答案。当您了解这些问题时,您的解决方案是否适用于给定的用例应该很明显。实际上,您没有描述解决方案如何解决问题 2、4 和 5。您可以使用我提到的一些库,也可以使用类似的想法/算法实现自己的解决方案。
在次要的技术说明中,请考虑为您的缓存层实现一个异步的、基于未来的 API,以便它返回Future[Entity]
而不是Loading | Entity
。