需求背景
在single-spa大的环境下,每一个子App都是一个独立的个体。独立意味着我们会有如下的几个需求:
- 每个应用程序拥有着自己的状态和变量,同时没有应用程序知道另一个应用程序或他们的数据模型的内部状态。总之,每个应用程序被视为黑盒,并且可以由不同的团队进行维护;
- 每个应用程序必须能够有一个复杂的状态;
- 当你的应用程序之间导航,状态不容有失(因为组件会挂载/卸载)。
解决思路
事件系统
为了满足这些要求,我已经决定了使用「事件系统」,其中每个应用程序可以选择是否监听其他应用程序发送的事件。这使得每个应用程序可以监听到其他应用程序发生了变化后,只修改自己的状态,以保持它们的隔离状态。
没有应用程序需要直接访问另一个应用程序的状态。
项目结构
此外,我需要的应用程序分为两个部分。一个是正常应用本身(GUI,框架等),另一个“通讯层”是独立模块,可以被门户(portal) 加载/实例化而与应用状态无关。
这允许每个应用程序监听和响应事件,即使它们没有挂载。
每个应用程序都可以按照自己喜欢的方式处理这些事件。唯一的要求是所有的应用程序都同意一个事件格式来发送和接收这些事件。
方案实现
Redux
对于这个例子,我决定使用redux,因为它基本上完全满足我的需要:
- 抛出事件
- 处理事件
tips:但是这个系统可以使用任何你喜欢的技术。
说明
概念图
接下来这是一个图表,它说明了实际发生了什么:
图表中的重要组成信息:
- StoreApps: 包含状态+业务逻辑。实现可在发生全局事件时由GlobalEventDistributor调用的调度方法。
- GUIApps: singleSPA 中间件+ UI代码 如 HTML, CSS, Controller等+框架如 React 或 Angular singleSPA中间件+ UI代码
- GlobalEventDistributor: 可以用来注册Store。向所有存储区发送调度事件。(观察者模式)
工作原理
- 根应用程序引导并加载所有存储区(stores)并实例化它们。这是必要的,因为我们需要通信层(存储区)始终处于活动状态。即使整个应用程序还没有挂载。否则,没有应用程序的特定事件得到处理。
- 当应用程序被挂载时,根应用程序向下传递属于各个应用程序的已实例化的存储区。根应用程序还将『对GlobalEventDistributor的引用』传递给应用程序。
- 现在,您可以使用dispatch()方法将所有全局事件发送到GlobalEventDistributor,并将所有其他事件发送到本地存储。
缺点
如前所述,最大的缺点是必须在根应用程序加载时加载所有存储。
这样做的原因是,我们正在构建一个项目,该项目将有一个巨大的应用程序状态完全存储在浏览器中。
用户可能会在没有任何服务器通信的情况下输入1h的数据,一旦完成,用户通过一次单击将会保存所有内容。
然而,这不一定是您的用例。例如,如果您只对与当前活动的任何应用程序的应用程序间通信感兴趣,您可能不需要预先加载所有状态,而是在应用程序运行时加载它们。