Dies ist der erste Artikel zu diesem Thema, insgesamt sind 3 geplant:
- * Erstellen Sie eine Root-Anwendung aus Ihrem vorhandenen Projekt und fügen Sie 3 Mikroanwendungen hinzu (vue, react, angle).
- Kommunikation zwischen Mikroanwendungen
- Arbeiten mit Git (Bereitstellung, Updates)
Inhaltsverzeichnis
- ein gemeinsamer Teil
- Warum wird es gebraucht?
- Erstellen Sie einen Root-Container (siehe Definition unten) aus Ihrem Monolithen
- Erstellen Sie eine VUE-Mikro-App (Vue-App)
- Erstellen Sie eine Mikroanwendung REACT (React-App)
- Erstellen Sie eine Mikroanwendung ANGULAR (Angular-App)
1. Allgemeiner Teil
Ziel dieses Artikels ist es, die Möglichkeit hinzuzufügen, ein vorhandenes monolithisches Projekt als Root-Container für eine Microservice-Architektur zu verwenden.
Das vorhandene Projekt basiert auf Winkel 9.
Für die Microservice-Architektur verwenden wir die Single-Spa- Bibliothek .
Sie müssen dem Stammprojekt 3 Projekte hinzufügen. Wir verwenden verschiedene Technologien: Vue-App, Angular-App, React-App (siehe S. 4, 5, 6).
Parallel zur Erstellung dieses Artikels versuche ich, diese Architektur in ein Produktionsprojekt zu implementieren, an dem ich gerade arbeite. Daher werde ich versuchen, alle Fehler, die ich während des Entwicklungsprozesses habe, und ihre Lösungen zu beschreiben.
Root-Anwendung (im Folgenden root) - die Wurzel (Container) unserer Anwendung. Wir werden alle unsere Microservices darin ablegen (registrieren). Wenn Sie bereits ein Projekt haben und diese Architektur darin implementieren möchten, ist Ihr vorhandenes Projekt die Stammanwendung. Von dort aus werden Sie im Laufe der Zeit versuchen, Teile Ihrer Anwendung herauszunagen, separate Microservices zu erstellen und in diesem Container zu registrieren.
Dieser Ansatz zum Erstellen eines Root-Containers bietet eine hervorragende Möglichkeit, ohne großen Aufwand auf eine andere Technologie zu migrieren.
Zum Beispiel haben wir uns entschlossen, komplett von eckig zu vue zu wechseln, aber das Projekt ist mutig und bringt im Moment viel Geld in das Geschäft.
Ohne Microservice-Architektur wäre dies nicht in unseren Gedanken aufgetaucht, nur für verzweifelte Menschen, die an Einhörner glauben und dass wir alle ein Hologramm sind.
Um in der Realität auf eine neue Technologie umzusteigen, muss das gesamte Projekt neu geschrieben werden, und nur dann können wir von seinem Auftreten im Kampf profitieren.
Eine weitere Option ist die Microservice-Architektur. Sie können ein Root-Projekt aus Ihrem Monolithen erstellen, dort ein neues Projekt auf demselben Vue hinzufügen, das Roaming auf Root einrichten und fertig. Sie können in den Kampf ziehen, nach und nach kleine Stücke aus der Wurzel des Projekts herausschneiden und sie auf Ihr Vue-Mikroprojekt übertragen. Dadurch bleiben nur die Dateien in Ihrem Stammcontainer, die zum Importieren Ihres neuen Projekts benötigt werden.
Dies kann genau hier und jetzt ohne Verlust und ohne Blut geschehen und ist vor allem real.
Ich werde Angular als Root verwenden, da das vorhandene Projekt darin geschrieben wurde.
Die allgemeine Schnittstelle, in die die Einzelseitenanwendung
eingebunden wird: bootstrap (mounter, bus) - wird aufgerufen, nachdem der Dienst geladen wurde. Sie gibt an, welches Element des Hauses Sie mounten müssen, gibt ihm einen Nachrichtenbus, den der Mikrodienst abonniert, und kann Anforderungen abhören und senden sowie den Befehl
mount () - Mounten der Anwendung von zu Hause aus
Unmount () - Zerlegen der Anwendung
Unload () - Entladen der Anwendung
Im Code werde ich die Funktionsweise jeder Methode noch einmal lokal am Verwendungsort beschreiben.
2. Warum wird es benötigt?
Beginnen wir an dieser Stelle streng in der richtigen Reihenfolge.
Es gibt zwei Arten von Architektur:
- Monolith
- Microservice-Architektur
Mit einem Monolithen ist alles ganz einfach und uns so vertraut wie möglich. Starker Zusammenhalt, riesige Codeblöcke, ein gemeinsames Repository, eine Reihe von Methoden.
Zu Beginn ist die monolithische Architektur so bequem und schnell wie möglich. Es gibt keine Probleme und Schwierigkeiten beim Erstellen von Integrationsdateien, Zwischenschichten, Ereignismodellen, Datenbussen usw.
Das Problem tritt auf, wenn Ihr Projekt wächst. Es erscheinen viele separate, komplexe Funktionen für verschiedene Zwecke. All diese Funktionen werden innerhalb des Projekts an gängige Modelle, Zustände, Dienstprogramme, Schnittstellen, Methoden usw. gebunden.
Außerdem wird die Anzahl der Verzeichnisse und Dateien im Projekt im Laufe der Zeit sehr groß. Es gibt Probleme, das Projekt als Ganzes zu finden und zu verstehen. Die "Draufsicht" geht verloren, was Klarheit darüber gibt, was wir tun, wo was liegt und wer es braucht.
Darüber hinaus ist Eaglesons Gesetz in Kraft , das besagt, dass Ihr Code, den Sie 6 Monate oder länger nicht angesehen haben, so aussieht, als hätte ihn jemand anderes geschrieben.
Das Schmerzlichste ist, dass alles exponentiell wächst, was dazu führt, dass Krücken beginnen, die aufgrund der Komplexität der Pflege des Codes in Verbindung mit den oben genannten und im Laufe der Zeit auftretenden Wellen unverantwortlicher Begriffe hinzugefügt werden müssen.
Wenn Sie ein Live-Projekt haben, das sich ständig weiterentwickelt, wird dies zu einem großen Problem, der ewigen Unzufriedenheit Ihres Teams, einer großen Anzahl von Mitarbeitern - Stunden, um geringfügige Änderungen am Projekt vorzunehmen, einer niedrigen Einstiegsschwelle für neue Mitarbeiter und viel Zeit, um das Projekt in den Kampf zu ziehen. Das alles führt zu Unordnung, na ja, wir lieben Ordnung?
Kommt das bei einem Monolithen immer vor?
Natürlich nicht! Es hängt alles von der Art Ihres Projekts ab, von den Problemen, die während der Teamentwicklung auftreten. Ihr Projekt ist möglicherweise nicht so groß, um eine komplexe Geschäftsaufgabe auszuführen. Dies ist normal und ich glaube, es ist richtig.
Zunächst müssen wir auf die Parameter unseres Projekts achten.
Ich werde versuchen, die Punkte herauszunehmen, anhand derer Sie verstehen können, ob wir wirklich eine Microservice-Architektur benötigen:
- 2 oder mehr Teams arbeiten an dem Projekt, die Anzahl der Front-End-Entwickler beträgt 10+;
- Ihr Projekt besteht aus zwei oder mehr Geschäftsmodellen. Sie verfügen beispielsweise über einen Online-Shop mit einer großen Menge an Waren, Filtern, Benachrichtigungen und der Funktionalität der Verteilung von Kurierdiensten (zwei separate, nicht kleine Geschäftsmodelle, die sich gegenseitig stören). All dies kann getrennt leben und ist nicht voneinander abhängig.
- Die Anzahl der UI-Funktionen wächst täglich oder wöchentlich, ohne den Rest des Systems zu beeinträchtigen.
Mikrofronts werden verwendet, um:
- Einzelne Teile des Frontends konnten unabhängig voneinander entwickelt, getestet und bereitgestellt werden.
- Teile des Frontends können ohne Zusammenbau hinzugefügt, entfernt oder ersetzt werden.
- .
- , - «», - ( ) -.
- ,
- .
single-spa ?
- (, React, Vue Angular) , .
- Single-spa , , .
- .
Nach meinem Verständnis ist Microservice eine unabhängige Einzelseitenanwendung, die nur eine Benutzeraufgabe löst. Diese Anwendung muss auch nicht das gesamte Teamproblem lösen.
SystemJS ist eine Open-Source-JS-Bibliothek, die üblicherweise als Polyfill für Browser verwendet wird.
Die Polyfüllung ist ein Teil des JS-Codes, der verwendet wird, um ältere Browser, die ihn nicht unterstützen, mit modernen Funktionen zu versorgen.
Eine der Funktionen von SystemJS ist die Importzuordnung, mit der Sie ein Modul über das Netzwerk importieren und einem Variablennamen zuordnen können.
Sie können beispielsweise eine Importzuordnung für eine React-Bibliothek verwenden, die über ein CDN geladen wird:
ABER!
Wenn Sie ein Projekt von Grund auf neu erstellen, obwohl Sie alle Parameter Ihres Projekts festgelegt haben, haben Sie sich für ein riesiges Mega-Superprojekt mit einem Team von mehr als 30 Mitarbeitern entschieden. Warten Sie eine Minute!
Ich mag die Idee des berüchtigten Begründers der Idee von Microservices - Martin Fowler - sehr .
Er schlug vor, monolithischen Ansatz und Microservices in einem (MonolithFirst) zu kombinieren. Die Hauptidee lautet wie folgt:
Sie sollten kein neues Projekt mit Microservices starten, auch wenn Sie sich sicher sind, dass die zukünftige Anwendung groß genug ist, um diesen Ansatz zu rechtfertigen
Ich werde hier auch die Nachteile einer solchen Architektur beschreiben:
- Eine Wechselwirkung zwischen Fragmenten kann mit Standardröhrchenmethoden (z. B. DI) nicht erreicht werden.
- Was ist mit allgemeinen Abhängigkeiten? Schließlich wächst die Größe der Anwendung sprunghaft, wenn sie nicht aus den Fragmenten herausgenommen wird.
- Jemand muss weiterhin für das Routing in der endgültigen Anwendung verantwortlich sein.
- Es ist nicht klar, was mit der Tatsache zu tun ist, dass sich verschiedene Mikrodienste in verschiedenen Domänen befinden können
- Was tun, wenn eines der Fragmente nicht verfügbar ist / nicht gerendert werden kann?
3. Erstellen eines Root-Containers
Genug Theorie, es ist Zeit zu beginnen.
Gehe zur Konsole
ng add single-spa-angular
npm i systemjs@6.1.4,
npm i -d @types/systemjs@6.1.0,
npm import-map-overrides@1.8.0
Importieren Sie in ts.config.app.json Deklarationen (Typen) global.
// ts.config.app.json
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": [
(+) "systemjs"
]
},
Fügen Sie app-routing.module.ts alle Mikroanwendungen hinzu, die wir root hinzufügen
// app-routing.module.ts
{
path: 'vue-app',
children: [
{
path: '**',
loadChildren: ( ) => import('./spa-host/spa-host.module').then(m => m.SpaHostModule),
data: { app: '@somename/vue-app' }
}
]
},
{
path: 'angular-app',
children: [
{
path: '**',
loadChildren: ( ) => import('./spa-host/spa-host.module').then(m => m.SpaHostModule),
data: { app: '@somename/angular-app' }
}
]
},
{
path: 'react-app',
children: [
{
path: '**',
loadChildren: ( ) => import('./spa-host/spa-host.module').then(m => m.SpaHostModule),
data: { app: '@somename/react-app' }
}
]
},
Sie müssen auch config hinzufügen
// extra-webpack.config.json
module.exports = (angularWebpackConfig, options) => {
return {
...angularWebpackConfig,
module: {
...angularWebpackConfig.module,
rules: [
...angularWebpackConfig.module.rules,
{
parser: {
system: false
}
}
]
}
};
}
Lassen Sie uns die Datei package.json ändern, alles hinzufügen, was für die Arbeit oder erforderlich ist
// package.json
"dependencies": {
...,
(+) "single-spa": "^5.4.2",
(+) "single-spa-angular": "^4.2.0",
(+) "import-map-overrides": "^1.8.0",
(+) "systemjs": "^6.1.4",
}
"devDependencies": {
...,
(+) "@angular-builders/custom-webpack": "^9",
(+) "@types/systemjs": "^6.1.0",
}
Fügen Sie die erforderlichen Bibliotheken zu angle.json hinzu
// angular.json
{
...,
"architect": {
"build": {
...,
"scripts": [
...,
(+) "node_modules/systemjs/dist/system.min.js",
(+) "node_modules/systemjs/dist/extras/amd.min.js",
(+) "node_modules/systemjs/dist/extras/named-exports.min.js",
(+) "node_modules/systemjs/dist/extras/named-register.min.js",
(+) "node_modules/import-map-overrides/dist/import-map-overrides.js"
]
}
}
},
Erstellen Sie einen einzelnen Spa- Ordner im Stammverzeichnis des Projekts . Fügen wir 2 Dateien hinzu.
1. route-reuse-Strategy.ts ist die Routing-Datei unserer Microservices.
Wenn eine untergeordnete Anwendung intern weitergeleitet wird, interpretiert diese Anwendung dies als Routenänderung.
Standardmäßig wird dadurch die aktuelle Komponente zerstört und durch eine neue Instanz derselben Spa-Host-Komponente ersetzt.
Diese Strategie zur Wiederverwendung von Routen untersucht routeData.app, um festzustellen, ob die neue Route als dieselbe Route wie die vorherige behandelt werden soll, und stellt sicher, dass die untergeordnete App nicht erneut bereitgestellt wird, wenn die angegebene untergeordnete App intern weitergeleitet wird.
// route-reuse-strategy.ts
import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
import { Injectable } from '@angular/core';
@Injectable()
export class MicroFrontendRouteReuseStrategy extends RouteReuseStrategy {
shouldDetach(): boolean {
//
return false;
}
store(): void { }
shouldAttach(): boolean {
return false;
}
//
retrieve(): DetachedRouteHandle {
return null;
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig || (future.data.app && (future.data.app === curr.data.app));
}
}
2. Service single-spa.service.ts
Der Service speichert die Methode zum Ein- und Aushängen von Micro-Frontend-Anwendungen.
mount ist eine Lebenszyklusfunktion, die aufgerufen wird, wenn die registrierte Anwendung nicht gemountet ist, ihre Aktivitätsfunktion jedoch true zurückgibt. Beim Aufruf sollte diese Funktion die URL überprüfen, um die aktive Route zu bestimmen und dann DOM-Elemente, DOM-Ereignisse usw. zu erstellen.
unmount ist eine Lebenszyklusfunktion, die aufgerufen wird, wenn eine registrierte Anwendung bereitgestellt wird, deren Aktivitätsfunktion jedoch false zurückgibt. Beim Aufruf sollte diese Funktion alle DOM-Elemente löschen.
//single-spa.service.ts
import { Injectable } from '@angular/core';
import { mountRootParcel, Parcel, ParcelConfig } from 'single-spa';
import { Observable, from, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class SingleSpaService {
private loadedParcels: {
[appName: string]: Parcel;
} = {};
mount(appName: string, domElement: HTMLElement): Observable<unknown> {
return from(System.import<ParcelConfig>(appName)).pipe(
tap((app: ParcelConfig) => {
this.loadedParcels[appName] = mountRootParcel(app, {
domElement
});
})
);
}
unmount(appName: string): Observable<unknown> {
return from(this.loadedParcels[appName].unmount()).pipe(
tap(( ) => delete this.loadedParcels[appName])
);
}
}
Als nächstes erstellen wir einen Verzeichniscontainer / app / spa-host .
Dieses Modul implementiert die Registrierung und Zuordnung unserer Micro-Frontend-Anwendungen zu root.
Fügen wir dem Modul 3 Dateien hinzu.
1. Das Modul selbst spa-host.module.ts
//spa-host.module.ts
import { RouterModule, Routes } from '@angular/router';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { SpaUnmountGuard } from './spa-unmount.guard';
import { SpaHostComponent } from './spa-host.component';
const routes: Routes = [
{
path: '',
canDeactivate: [SpaUnmountGuard],
component: SpaHostComponent,
},
];
@NgModule({
declarations: [SpaHostComponent],
imports: [CommonModule, RouterModule.forChild(routes)]
})
export class SpaHostModule {}
2. Komponente spa-host.component.ts - koordiniert die Installation und Demontage von Micro-Frontend-Anwendungen
// spa-host.component.ts
import { Component, OnInit, ViewChild, ElementRef, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import {SingleSpaService} from '../../single-spa/single-spa.service';
@Component({
selector: 'app-spa-host',
template: '<div #appContainer></div>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpaHostComponent implements OnInit {
@ViewChild('appContainer', { static: true })
appContainerRef: ElementRef;
appName: string;
constructor(private singleSpaService: SingleSpaService, private route: ActivatedRoute) { }
ngOnInit() {
//
this.appName = this.route.snapshot.data.app;
this.mount().subscribe();
}
//
mount(): Observable<unknown> {
return this.singleSpaService.mount(this.appName, this.appContainerRef.nativeElement);
}
//
unmount(): Observable<unknown> {
return this.singleSpaService.unmount(this.appName);
}
}
3. spa-unmount.guard.ts - Überprüft, ob der Anwendungsname in der Route unterschiedlich ist. Analysieren Sie den vorherigen Dienst. Wenn dies auch der Fall ist, rufen Sie ihn einfach auf.
// spa-unmount.guard.ts
import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { SpaHostComponent } from './spa-host.component';
@Injectable({ providedIn: 'root' })
export class SpaUnmountGuard implements CanDeactivate<SpaHostComponent> {
canDeactivate(
component: SpaHostComponent,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState: RouterStateSnapshot
): boolean | Observable<boolean> {
const currentApp = component.appName;
const nextApp = this.extractAppDataFromRouteTree(nextState.root);
if (currentApp === nextApp) {
return true;
}
return component.unmount().pipe(map(_ => true));
}
private extractAppDataFromRouteTree(routeFragment: ActivatedRouteSnapshot): string {
if (routeFragment.data && routeFragment.data.app) {
return routeFragment.data.app;
}
if (!routeFragment.children.length) {
return null;
}
return routeFragment.children.map(r => this.extractAppDataFromRouteTree(r)).find(r => r !== null);
}
}
Wir registrieren alles, was wir der app.module hinzugefügt haben
// app.module.ts
providers: [
...,
{
(+) provide: RouteReuseStrategy,
(+) useClass: MicroFrontendRouteReuseStrategy
}
]
Lassen Sie uns main.js ändern.
// main.ts
import { enableProdMode, NgZone } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { start as singleSpaStart } from 'single-spa';
import { getSingleSpaExtraProviders } from 'single-spa-angular';
import { AppModule } from './app/app.module';
import { PlatformLocation } from '@angular/common';
if (environment.production) {
enableProdMode();
}
singleSpaStart();
//
const appId = 'container-app';
// , getSingleSpaExtraProviders.
platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule).then(module => {
NgZone.isInAngularZone = () => {
// @ts-ignore
return window.Zone.current._properties[appId] === true;
};
const rootPlatformLocation = module.injector.get(PlatformLocation) as any;
const rootZone = module.injector.get(NgZone);
// tslint:disable-next-line:no-string-literal
rootZone['_inner']._properties[appId] = true;
rootPlatformLocation.setNgZone(rootZone);
})
.catch(err => {});
Als Nächstes erstellen wir eine Datei import-map.json im Freigabeordner. Die Datei wird zum Hinzufügen von Importkarten benötigt.
Im Moment wird es leer sein und sich füllen, wenn Anwendungen zu root hinzugefügt werden.
<head>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My first microfrontend root project</title>
<base href="/">
...
(+) <meta name="importmap-type" content="systemjs-importmap" />
<script type="systemjs-importmap" src="/assets/import-map.json"></script>
</head>
<body>
<app-root></app-root>
<import-map-overrides-full></import-map-overrides-full>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>
4. Erstellen Sie eine VUE-Mikroanwendung (vue-app)
Nachdem wir unserem monolithischen Projekt die Möglichkeit hinzugefügt haben, eine Root-Anwendung zu werden, ist es an der Zeit, unsere erste externe Mikroanwendung mit Single-Spa zu erstellen.
Zunächst müssen wir create-single-spa global installieren, eine Befehlszeilenschnittstelle, mit der wir neue Single-Spa-Projekte mit einfachen Befehlen erstellen können.
Gehe zur Konsole
npm install --global create-single-spa
Erstellen Sie eine einfache Vue-App mit einem Befehl in der Konsole
create-single-spa
Über die Befehlszeilenschnittstelle werden Sie aufgefordert, ein Verzeichnis, einen Projektnamen, eine Organisation und einen Anwendungstyp auszuwählen, der erstellt werden soll
? Directory for new project vue-app
? Select type to generate single-spa application / parcel
? Which framework do you want to use? vue
? Which package manager do you want to use? npm
? Organization name (use lowercase and dashes) somename
Wir starten unsere Mikroanwendung
npm i
npm run serve --port 8000
Wenn wir den Pfad im Browser localhost eingeben : 8080 / , sehen wir im Fall von vue einen leeren Bildschirm. Was ist passiert?
Da die generierte Mikro-App keine index.js-Datei enthält.
Single-Spa bietet einen Spielplatz, von dem aus Sie die Anwendung über das Internet herunterladen können. Verwenden Sie sie also zuerst.
Zu index.js hinzufügen
single-spa-playground.org/playground/instant-test?name=@some-name/vue-app&url=8000Beim Erstellen der Root-Anwendung haben wir im Voraus eine Map hinzugefügt, um unser vue-Projekt zu laden.
{
"imports": {
... ,
"vue": "https://unpkg.com/vue",
"vue-router": "https://cdn.jsdelivr.net/npm/vue-router@3.0.7/dist/vue-router.min.js",
"@somename/vue-app": "//localhost:8080/js/app.js"
}
}
Bereit! Jetzt können wir aus unserem Winkelwurzelprojekt in vue geschriebene Mikroanwendungen laden.
5. Erstellen Sie eine Mikroanwendung REACT (React-App)
Mit dem Befehl in der Konsole erstellen wir eine ähnlich einfache Reaktionsanwendung
create-single-spa
Organisationsname: Somename
Projektname : React -App
? Directory for new project react-app
? Select type to generate single-spa application / parcel
? Which framework do you want to use? react
? Which package manager do you want to use? npm
? Organization name (use lowercase and dashes) somename
Lassen Sie uns überprüfen, ob wir in unserer Stammanwendung eine Importzuordnung hinzugefügt haben
{
"imports": {
... ,
"react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.development.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.development.js",
"@somename/react-app": "//localhost:8081/somename-projname.js",
}
}
Erledigt! Jetzt laden wir auf unserer React-App-Route das React-Mikroprojekt.
6. Erstellen Sie eine Mikroanwendung ANGULAR (Angular-App)
Wir erstellen eine Angular-Mikroanwendung genauso wie die vorherigen 2
create-single-spa
Organisationsname: Somename
Projektname : Angular -App
? Directory for new project angular-app
? Select type to generate single-spa application / parcel
? Which framework do you want to use? angular
? Which package manager do you want to use? npm
? Organization name (use lowercase and dashes) somename
Lassen Sie uns überprüfen, ob wir in unserer Stammanwendung eine Importzuordnung hinzugefügt haben
{
"imports": {
... ,
"@somename/angular-app": "//localhost:8082/main.js",
}
}
Wir starten, überprüfen, alles sollte funktionieren.
Dies ist mein erster Beitrag auf Habré, ich werde für Ihre Kommentare sehr dankbar sein.