The goal of this project is to provide an example of how to build a portal like app which consists of multiple single page applications (SPA's). Each SPA should be self contained with its own build process. It should be individually deployable without the need to deploy the whole application if there are changes to any individual app.
This example is based on simple-single-spa-webpack-example but will provide further features like:
npm install
npm run watch:portal
npm install
npm run watch
watch:portal
: Builds the app as UMD module with singleSPA as middleware to consume the app by the portal. Changes are automatically detected.
build:portal
: Releases the app as UMD module and outputs all contents to a folder. You can upload the produced file in production to your webserver. Hint: The Angular 6 example is being build with AOT. You can use npm run build:portal -- --env.analyzeBundle
to see that there is no compiler.js inside the bundle.
watch:standalone
: If you just want to develop the single app without the whole portal, you can use this task. Check the console log to see which port the app is being served from. This task is OPTIONAL! For now this task only exists on the Vue project to serve as an example.
build:standalone
: As with the watch:standlone
taks, this builds the app as stand alone version (no portal needed). This task is OPTIONAL! For now this task only exists on the Vue project to serve as an example.
This topic has been discussed multiple times (i.e. here or here). There may be many solutions to solve this problem. In this repository I want you to show a solution that meets the following requirements:
To meet these requirements I have decided for an event system where each app can or can not listen to events that other apps send. This enables each app to keep their isolated state and modify only their own state based on events from other apps (and probably resend other events). No app needs direct access to the state of another app.
Furthermore I needed to split the apps into two parts. One is the normal app itself (GUI, Framework, etc.), the other is a "communication layer" which is exported as separate module and loaded/instantiated by the portal regardless of the app state. This allows each app to listen and react to events even if they aren't mounted.
Each app can process these events in whatever way they like. The only requirement is that all apps agree on one event format to send and receive these events.
For this example I have decided to just go with redux since it basically does exactly what I need. Throw events and process events. But this system works with whatever technic you like.
Here is a graphic which illustrates what actually happens:
StoreApps: Contains state + business logic. Implements a dispatch method which can be called by the GlobalEventDistributor if an global event occurs.
GUIApps: singleSPA middleware + UI code like HTML, CSS, Controller, etc. + Framework like React or Angular
GlobalEventDistributor: Can be used to register stores. Sends dispatch events to all stores. (Observer pattern)
As already mentioned, the biggest disadvantage is that all stores have to be loaded when the root-application loads. The reason for this is that we are building a project that will have a huge application state being entirely in the browser. The user will likely input 1h of data without any server communication and once he is done, he will save everything with one click.This must not necessarily be your use-case. For example if you are only interested in inter-app-communication with any currently active app, you may not need to load all states beforehand but rather load them while the apps mount.
The big issue with Angular 2+ is, that it (or third party libraries which Angular depends on) pollute the global window object. One such library is Zone.js. Zone.js monkey patches all async events and add its reference to the window object. If you have multiple Angular apps running, Angular will complain that Zone.js is already loaded.
One possible solution is to separate Zone.js from all Angular apps and load Zone.js only once in the portal. This may not be the best solution because as soon as you have multiple different Angular versions as apps in the portal, it may be possible that some of them require different Zone.js versions. Which may break everything at that point. Even though it is not a great solution, it may be the best solution we have with the current state of angular.
The other solution I found is to load every Angular app in its own iframe. Doing that, every Angular app runs completely isolated. You can then set the render target for Angular to the parent window. With this solution Angular runs in a complete isolated context but renders all content to the main DOM. Sadly this is also not the perfect solution since you open up many other issues you have to deal with. A few examples are:
In the future we may have better solutions like Angular elements to deal with this issue, until then our best bet is to put a single Zone.js instance into the portal app. Which is exactly what I did in this example project.
框架 客户端框架 下列框架实现了这种(或类似的)模式: Piral Open Components qiankun Luigi Frint.js 服务端框架 下列框架实现这种(或类似的)模式: Mosaic PuzzleJs Podium Micromono 帮助(Helper)库 帮助库或是提供共享依赖、路由事件的基础架构,或是将不同的微前端及其生命周期组织起来 。 下面的库可用于削减模板代码:
介绍 RxJava(以及它派生出来的RxGroovy和RxScala)中有一个名为Single的Observable变种。 Single类似于Observable,不同的是,它总是只发射一个值,或者一个错误通知,而不是发射一系列的值。 因此,不同于Observable需要三个方法onNext, onError, onCompleted,订阅Single只需要两个方法: onSuccess - Si
检查列表是否只有一个元素并返回它。 语法 (Syntax) List.single 例子 (Example) void main() { var lst = new List(); lst.add(12); print("The list has only one element: ${lst.single}"); } 它将产生以下output - The lis
single 限制 Observable 只有一个元素,否出发出一个 error 事件 single 操作符将限制 Observable 只产生一个元素。如果 Observable 只有一个元素,它将镜像这个 Observable 。如果 Observable 没有元素或者元素数量大于一,它将产生一个 error 事件。
Single Single 是 Observable 的另外一个版本。不像 Observable 可以发出多个元素,它要么只能发出一个元素,要么产生一个 error 事件。 发出一个元素,或一个 error 事件 不会共享附加作用 一个比较常见的例子就是执行 HTTP 请求,然后返回一个应答或错误。不过你也可以用 Single 来描述任何只有一个元素的序列。 如何创建 Single 创建 Sing
single 函数签名: single(a: Function): Observable 发出通过表达式的单一项。 示例 示例 1: 发出通过断言的第一个数字 ( StackBlitz | jsBin | jsFiddle ) // RxJS v6+ import { from } from 'rxjs'; import { single } from 'rxjs/operators'; //