17/09/2023
In der modernen Webentwicklung stehen Teams oft vor der Herausforderung, neue Technologien zu integrieren, ohne bestehende, stabile Anwendungen komplett neu schreiben zu müssen. Dies führt nicht selten zu einem Dilemma: Wie kann man Innovation vorantreiben und gleichzeitig die Wartbarkeit und Skalierbarkeit komplexer Anwendungen gewährleisten? Die Antwort liegt in der Architektur von Micro-Frontends, und single-spa ist ein herausragendes Werkzeug, das genau diese Vision Wirklichkeit werden lässt. Es erlaubt Ihnen, unabhängige Front-End-Anwendungen, die jeweils mit ihrem eigenen JavaScript-Framework geschrieben sind, auf derselben Seite koexistieren zu lassen.

Stellen Sie sich vor, Sie könnten die Vorteile von React für neue Funktionen nutzen, während Ihr bewährter AngularJS-Code weiterhin reibungslos läuft. Mit single-spa ist dies nicht nur eine Vision, sondern eine greifbare Realität. Dieser Artikel führt Sie durch die Grundlagen und die Implementierung von single-spa, wobei der Fokus auf der Integration von React- und AngularJS-Anwendungen liegt, die nahtlos zusammenarbeiten.
- Die revolutionären Vorteile von single-spa
- Grundlagen der single-spa Architektur
- Projekt-Setup: Die ersten Schritte
- Das Master-HTML-Dokument (index.html)
- Die React-Anwendungen: Home & NavBar
- Die AngularJS-Anwendung: Ein tieferer Einblick
- Vergleichstabelle: single-spa Helferbibliotheken
- Häufig gestellte Fragen (FAQs)
- F: Kann ich single-spa mit anderen Frameworks nutzen?
- F: Muss ich Webpack verwenden, um single-spa zu nutzen?
- F: Wie werden Anwendungen voneinander isoliert und wie vermeiden sie DOM-Konflikte?
- F: Was ist der Unterschied zwischen loadingFunction und activityFunction?
- F: Wie funktioniert die Navigation zwischen den einzelnen Micro-Frontends?
- Fazit
Die revolutionären Vorteile von single-spa
Die Entscheidung für eine Micro-Frontend-Architektur mit single-spa bringt eine Vielzahl von Vorteilen mit sich, die die Entwicklung von großen und komplexen Webanwendungen grundlegend verändern können:
Framework-Unabhängigkeit und Flexibilität
Einer der größten Vorteile von single-spa ist die Fähigkeit, verschiedene Frameworks auf derselben Seite zu verwenden. Das bedeutet, dass Sie nicht an ein einziges Framework gebunden sind. Ihr Team kann die beste Technologie für jede spezifische Aufgabe wählen oder schrittweise auf modernere Frameworks umsteigen, ohne die gesamte Anwendung umschreiben zu müssen. Dies fördert Experimentierfreude und ermöglicht es, neue Frameworks und Bibliotheken zu erproben, während der Großteil der Anwendung stabil bleibt.
Agilität und Team-Autonomie
Micro-Frontends fördern die Autonomie von Entwicklungsteams. Jedes Team kann für seine spezifische Micro-Anwendung verantwortlich sein, von der Entwicklung bis zum Deployment. Dies reduziert Abhängigkeiten zwischen Teams, beschleunigt die Entwicklung und ermöglicht es, Code mit einem neuen Framework zu schreiben, ohne die bestehende Anwendung neu zu gestalten. Die Trennung der Codebasen führt zu kleineren, besser handhabbaren Projekten und somit zu schnelleren Iterationszyklen.
Optimierte Ladezeiten durch Lazy Loading
Ein weiterer entscheidender Vorteil ist die Möglichkeit, Code nur bei Bedarf zu laden (Lazy Loading). Anstatt beim ersten Laden der Seite den gesamten Code aller Anwendungen herunterzuladen, lädt single-spa nur die Teile, die für die aktuell angezeigte Ansicht relevant sind. Dies führt zu erheblich verbesserten anfänglichen Ladezeiten und einer insgesamt reaktionsfreudigeren Benutzererfahrung, da unnötiger Code nicht im Voraus geladen werden muss.
Grundlagen der single-spa Architektur
Im Kern von single-spa steht eine einfache, aber mächtige Idee: Ein zentraler Konfigurationspunkt, der festlegt, wann und wie einzelne Anwendungen geladen, gestartet und beendet werden. Dieser Konfigurationspunkt wird als single-spa-Konfiguration oder "Master-Controller" bezeichnet.
Die Rolle von registerApplication und start()
Die Funktion registerApplication ist das Herzstück der Anwendung. Sie wird verwendet, um single-spa mitzuteilen, welche Anwendungen existieren und wie sie verwaltet werden sollen. Jeder Aufruf von registerApplication benötigt drei Hauptparameter:
- Anwendungsname: Ein eindeutiger Name für die Micro-Anwendung.
loadingFunction: Eine Funktion, die ein Promise zurückgibt und den Code der Anwendung lädt. Dies ist der Punkt, an dem Lazy Loading ins Spiel kommt, da der Code erst bei Bedarf geladen wird.activityFunction: Eine reine Funktion, diewindow.locationals Argument erhält und einen Wahrheitswert zurückgibt. Dieser Wert bestimmt, ob die Anwendung auf der aktuellen URL aktiv sein sollte.
Nachdem alle Anwendungen registriert wurden, muss die Funktion start() aufgerufen werden. Vor dem Aufruf von start() werden Anwendungen zwar in den Browser geladen, aber nicht gebootstrappt, gemountet oder unmounted. start() aktiviert den Lebenszyklus von single-spa und ermöglicht die Interaktion der Anwendungen mit dem DOM.
Projekt-Setup: Die ersten Schritte
Um ein single-spa-Projekt zu starten, beginnen wir mit einer grundlegenden Verzeichnisstruktur und den notwendigen Abhängigkeiten. Dies schafft das Fundament für unsere Micro-Frontends.
Verzeichnisstruktur und Paketverwaltung
Zunächst erstellen Sie ein Hauptverzeichnis für Ihr Projekt und darin einen src/-Ordner, der alle Micro-Service-Anwendungen aufnehmen wird. Anschließend initialisieren Sie Ihr Projekt mit einem Paketmanager (hier yarn oder npm) und installieren single-spa als Abhängigkeit:
mkdir single-spa-simple-example && cd single-spa-simple-example yarn init yarn add single-spa mkdir srcBabel Konfiguration
Um unseren JavaScript-Code zu kompilieren, verwenden wir Babel. Es ist wichtig, die notwendigen Babel-Pakete und eine .babelrc-Datei zu konfigurieren, die festlegt, wie der Code transformiert werden soll, insbesondere für moderne JavaScript-Features und React-Syntax:
yarn add --dev @babel/core @babel/preset-env @babel/preset-react @babel/plugin-syntax-dynamic-import @babel/plugin-proposal-object-rest-spreadDie .babelrc-Datei enthält die Presets (@babel/preset-env für Browser-Kompatibilität und @babel/preset-react für JSX) und Plugins (für dynamische Imports und Objekt-Rest/Spread-Syntax), die für die Kompilierung unserer Anwendungen erforderlich sind.
Webpack Integration
Webpack wird als Modul-Bundler verwendet, um unseren Code zu bündeln und für den Browser vorzubereiten. Obwohl single-spa nicht zwingend Webpack erfordert, ist es eine gängige Wahl und wird in diesem Beispiel verwendet. Wir installieren Webpack selbst, seinen Development-Server und die CLI-Tools sowie wichtige Plugins und Loader:
yarn add webpack webpack-dev-server webpack-cli --dev yarn add clean-webpack-plugin --dev yarn add style-loader css-loader html-loader babel-loader --devDie webpack.config.js-Datei definiert, wie Webpack unseren Code verarbeitet. Sie legt den Einstiegspunkt fest (unsere single-spa.config.js), den Ausgabeort der gebündelten Dateien und die Regeln für verschiedene Dateitypen (z.B. CSS, JS, HTML). Ein wichtiger Bestandteil ist der publicPath, der angibt, wo die gebündelten Assets vom Server bereitgestellt werden.
package.json Skripte
Um die Entwicklung zu vereinfachen, fügen wir Skripte zu unserer package.json-Datei hinzu. Diese ermöglichen es uns, den Webpack Development Server zu starten und einen Produktions-Build zu erstellen:
"scripts": { "start": "webpack-dev-server --open", "build": "webpack --config webpack.config.js -p" }Das Master-HTML-Dokument (index.html)
Das index.html-Dokument ist das primäre HTML-Gerüst, das alle unsere Micro-Frontends hostet. Es ist entscheidend, dass jede Anwendung einen eigenen DOM-Mount-Punkt erhält, um Konflikte zu vermeiden und die Unabhängigkeit zu gewährleisten.
Für unsere Beispielanwendungen (home, navBar, angularJS) erstellen wir jeweils ein div-Element mit einer eindeutigen ID:
<body> <div id="navBar"></div> <div id="home"></div> <div id="angularJS"></div> </body>Um ein einheitliches Styling zu gewährleisten, binden wir globale Stylesheets und JavaScript-Bibliotheken wie Materialize CSS und jQuery direkt in index.html ein. Wichtig ist auch das Skript-Tag, das unsere single-spa.config.js-Datei lädt, da diese der zentrale Kontrollpunkt für alle Micro-Frontends ist.
Die Integration von React-Anwendungen in single-spa wird durch die Helferbibliothek single-spa-react erheblich vereinfacht. Diese Bibliothek implementiert die notwendigen Lebenszyklus-Funktionen für React, sodass Sie sich nicht um die Details kümmern müssen.

Die Home-Anwendung
Die Home-Anwendung ist eine React-App, die React Router für die Navigation verwendet. Wir installieren die benötigten Pakete:
yarn add react react-dom single-spa-react react-router-dom react-transition-groupIm src/home/home.app.js definieren wir die single-spa-Lebenszyklus-Methoden (bootstrap, mount, unmount) unter Verwendung von single-spa-react. Eine domElementGetter-Funktion gibt das DOM-Element zurück, in das die React-Anwendung gemountet werden soll (document.getElementById("home")).
Die root.component.js enthält die eigentliche React-Anwendung, die React Router zur Handhabung von Routen nutzt. Es ist wichtig, den basename-Prop des Routers auf /home zu setzen, um die Routen dieser spezifischen Anwendung korrekt zu isolieren, wie in der activityFunction in single-spa.config.js definiert.
Die NavBar-Anwendung ist ebenfalls eine React-App und dient als Top-Level-Navigation. Ihr Hauptmerkmal ist, dass sie immer angezeigt werden soll, unabhängig von der aktuellen Route. Dies wird durch eine activityFunction erreicht, die immer true zurückgibt:
registerApplication( 'navBar', () => import('./src/navBar/navBar.app.js').then(module => module.navBar), () => true );Die Implementierung ähnelt der Home-App, verwendet aber ebenfalls single-spa-react. Für die Navigation zwischen den Micro-Frontends wird die Utility-Funktion navigateToUrl von single-spa verwendet. Durch das Aufrufen dieser Funktion in den onClick-Handlern der Navigationslinks wird single-spa über die URL-Änderung informiert und kann die entsprechenden Anwendungen aktivieren oder deaktivieren.
Die AngularJS-Anwendung: Ein tieferer Einblick
Die Integration einer AngularJS-Anwendung (Version 1.x) in ein single-spa-Ökosystem ist ein hervorragendes Beispiel für die Framework-Unabhängigkeit, die single-spa bietet. Für diese Integration nutzen wir angular-ui-router für das Routing innerhalb der AngularJS-App und die Helferbibliothek single-spa-angularjs.
Abhängigkeiten installieren
Zunächst müssen die erforderlichen Pakete für AngularJS und seine Integration installiert werden:
yarn add angular angular-ui-router single-spa-angularjsRegistrierung in single-spa.config.js
Die AngularJS-Anwendung wird wie jede andere single-spa-Anwendung registriert. Um die activityFunction flexibler zu gestalten, definieren wir eine Hilfsfunktion pathPrefix, die prüft, ob der URL-Pfad mit einem bestimmten Präfix beginnt. Dies macht die Konfiguration übersichtlicher:
function pathPrefix(prefix) { return function(location) { return location.pathname.startsWith(prefix); } } registerApplication( "angularJS", () => import("./src/angularJS/angularJS.app.js"), pathPrefix('/angularJS') );Lebenszyklus-Management mit single-spa-angularjs
Die Datei src/angularJS/angularJS.app.js ist der zentrale Punkt für die single-spa-Integration der AngularJS-Anwendung. Hier wird single-spa-angularjs importiert und konfiguriert:
import singleSpaAngularJS from "single-spa-angularjs"; import angular from "angular"; import "./app.module.js"; import "./routes.js"; const domElementGetter = () => document.getElementById("angularJS"); const angularLifecycles = singleSpaAngularJS({ angular, domElementGetter, mainAngularModule: "angularJS-app", uiRouter: true, preserveGlobal: false, }); export const bootstrap = [angularLifecycles.bootstrap]; export const mount = [angularLifecycles.mount]; export const unmount = [angularLifecycles.unmount];Wichtige Konfigurationsoptionen sind hier:
angular: Die Angular-Instanz, die von der Anwendung verwendet wird.domElementGetter: Eine Funktion, die das DOM-Element zurückgibt, in das die AngularJS-Anwendung gemountet werden soll.mainAngularModule: Der Name des Hauptmoduls Ihrer AngularJS-Anwendung, das von single-spa gestartet werden soll.uiRouter: Ein Flag, das angibt, obangular-ui-routerverwendet wird, damitsingle-spa-angularjsdie Router-Hooks korrekt handhaben kann.preserveGlobal: Bestimmt, ob die globale Angular-Instanz während des Unmountings beibehalten werden soll.
Aufbau der AngularJS-Anwendung
Die AngularJS-Anwendung selbst wird in mehreren Dateien strukturiert:
app.module.js: Definiert das Hauptmodul der Anwendung (angularJS-app) und deklariert die Abhängigkeit zuui.router.root.component.jsundroot.template.html: Bilden die Wurzelkomponente der AngularJS-Anwendung.root.template.htmlenthält das grundlegende Layout, Überschriften und Navigationslinks, die auf interne Routen der AngularJS-App verweisen. Hier wird auch ein<ui-view />-Element platziert, in das die Unterkomponenten vonangular-ui-routergeladen werden.gifs.component.jsundgifs.template.html: Ein Beispiel für eine interne Komponente, die Daten von einer externen API (Giphy) abruft und anzeigt. Dies demonstriert die Fähigkeit der Micro-Anwendung, eigenständige Funktionen zu implementieren.
In-App-Routing mit angular-ui-router
Die Datei src/angularJS/routes.js konfiguriert den angular-ui-router, um die Navigation innerhalb der AngularJS-Anwendung zu verwalten. Es werden Zustände (States) definiert, die URL-Pfade mit Komponenten verknüpfen:
import angular from "angular"; import "./root.component.js"; import "./gifs.component.js"; angular.module("angularJS-app").config(($stateProvider, $locationProvider) => { $locationProvider.html5Mode({ enabled: true, requireBase: false, }); $stateProvider .state("root", { url: "/angularJS", template: "<root />", }) .state("root.gifs", { url: "/gifs", template: "<gifs />", }); });Die Aktivierung des html5Mode ermöglicht saubere URLs ohne Hash-Zeichen. Die Zustände root und root.gifs definieren, welche Komponenten bei den URLs /angularJS und /angularJS/gifs geladen werden sollen. Die url-Definitionen müssen dabei das Präfix berücksichtigen, das in der activityFunction für die AngularJS-Anwendung festgelegt wurde.
Vergleichstabelle: single-spa Helferbibliotheken
Die single-spa-Helferbibliotheken sind entscheidend für die einfache Integration gängiger Frameworks. Hier ist ein Vergleich der für React und AngularJS verwendeten Helfer:
| Merkmal | single-spa-react | single-spa-angularjs |
|---|---|---|
| Ziel-Framework | React | AngularJS (1.x) |
| Erforderliche Instanzen | React, ReactDOM | angular |
| DOM-Mount-Punkt | domElementGetter Funktion | domElementGetter Funktion |
| Hauptkomponente/Modul | rootComponent | mainAngularModule (Modulname) |
| Router-Integration | Handhabung über React Router (basename) | uiRouter: true (für angular-ui-router) |
| Zusätzliche Optionen | (Keine spezifischen im Beispiel) | preserveGlobal |
Häufig gestellte Fragen (FAQs)
F: Kann ich single-spa mit anderen Frameworks nutzen?
A: Ja, absolut! single-spa ist framework-agnostisch. Neben React und AngularJS gibt es offizielle Helferbibliotheken und Beispiele für Angular (2+), Vue.js, Svelte und andere. Die Grundprinzipien von registerApplication und den Lebenszyklus-Hooks bleiben gleich, nur die spezifische Implementierung innerhalb der Helferbibliothek ändert sich.
F: Muss ich Webpack verwenden, um single-spa zu nutzen?
A: Nein, Webpack ist nicht zwingend erforderlich. single-spa kann mit nahezu jedem Build-System oder JavaScript-Framework verwendet werden. Das Beispiel hier nutzt Webpack, da es eine weit verbreitete und leistungsfähige Lösung für das Bündeln von JavaScript-Anwendungen ist, insbesondere im Hinblick auf Code-Splitting und Lazy Loading.
F: Wie werden Anwendungen voneinander isoliert und wie vermeiden sie DOM-Konflikte?
A: Jede Micro-Anwendung wird in ein separates div-Element im Haupt-HTML-Dokument gemountet. Dadurch wird sichergestellt, dass sie den DOM-Bereich der anderen Anwendungen nicht direkt beeinflussen können. single-spa verwaltet auch die Lebenszyklen der Anwendungen so, dass sie gebootstrappt, gemountet und unmounted werden, wenn sie aktiv werden oder inaktiv werden, was weitere Konflikte verhindert.
F: Was ist der Unterschied zwischen loadingFunction und activityFunction?
A: Die activityFunction ist eine synchrone Funktion, die bestimmt, wann eine Anwendung aktiv sein soll, typischerweise basierend auf der aktuellen URL (window.location). Sie gibt einen Wahrheitswert zurück. Die loadingFunction hingegen ist eine asynchrone Funktion, die ein Promise zurückgibt. Sie ist dafür verantwortlich, den Code der Anwendung zu laden, wenn single-spa feststellt, dass die Anwendung aktiv werden muss. Dies ermöglicht Lazy Loading.
A: single-spa bietet die Utility-Funktion navigateToUrl, die eine einfache Navigation zwischen registrierten Anwendungen ermöglicht. Diese Funktion aktualisiert die Browser-URL und informiert single-spa, woraufhin die entsprechenden activityFunctions der registrierten Anwendungen neu bewertet werden und single-spa die notwendigen Lebenszyklus-Hooks ausführt (z.B. eine inaktive App unmounten und eine aktive App mounten). Alternativ kann auch pushState() direkt verwendet werden.
Fazit
single-spa bietet eine elegante und leistungsstarke Lösung für die Implementierung von Micro-Frontends. Die Fähigkeit, Anwendungen unterschiedlicher Frameworks auf derselben Seite zu betreiben, revolutioniert die Art und Weise, wie komplexe Webanwendungen entwickelt und gewartet werden. Durch die klare Trennung von Verantwortlichkeiten, die Optimierung der Ladezeiten und die Förderung der Team-Agilität ist single-spa ein unverzichtbares Werkzeug für moderne Entwicklerteams. Das gezeigte Beispiel mit React und AngularJS demonstriert eindrucksvoll die Flexibilität und Skalierbarkeit, die diese Architektur bietet. Es ist ein klarer Weg in eine Zukunft, in der Webanwendungen nicht nur leistungsfähiger, sondern auch einfacher zu entwickeln und zu pflegen sind.
Wenn du andere Artikel ähnlich wie Micro-Frontends: AngularJS trifft React mit single-spa kennenlernen möchtest, kannst du die Kategorie Wellness besuchen.
