Geteilte Abhängigkeiten und Runtime-Integration ohne Rebuild
Module Federation lädt Vue-Komponenten erst im Browser aus getrennt deployten Anwendungen nach, statt sie zur Build-Zeit einzubinden. Das ermöglicht unabhängige Deployments über Teamgrenzen hinweg, verlangt aber eine bewusste Strategie für geteilte Abhängigkeiten, Versionskonflikte und Fallbacks, wenn ein Remote nicht erreichbar ist.
Inhaltsverzeichnis
- 1. Was Module Federation für Vue-Apps bedeutet
- 2. Host und Remote: die Grundkonfiguration
- 3. Exposes: Vue-Komponenten gezielt freigeben
- 4. Shared Dependencies richtig konfigurieren
- 5. Versionierung und Kompatibilitätsverträge
- 6. Fallback-Strategien bei nicht erreichbaren Remotes
- 7. TypeScript-Typen über Remote-Grenzen hinweg
- 8. Performance: Preloading und Bundle-Größe
- 9. Module Federation im Werkzeugvergleich
- 10. Zusammenfassung
- 11. FAQ
1. Was Module Federation für Vue-Apps bedeutet
Module Federation ist eine Bundler-Funktion, ursprünglich aus Webpack 5, mittlerweile auch über vite-plugin-federation für Vite-Projekte verfügbar, die es erlaubt, JavaScript-Module zur Laufzeit aus einer anderen, unabhängig gebauten Anwendung zu laden. Für Vue-Apps bedeutet das konkret: Eine Host-Anwendung kann eine Vue-Komponente aus einer völlig separat deployten Remote-Anwendung importieren, als wäre sie ein lokales Modul, ohne dass die Host-Anwendung zur Build-Zeit von der Remote-Codebasis abhängt.
Der entscheidende Unterschied zu klassischen npm-Paketen liegt im Zeitpunkt der Bindung. Ein npm-Paket wird zur Build-Zeit fest in den Host-Bundle eingebettet, jede Änderung erfordert einen neuen Host-Build. Module Federation in Vue-Apps löst diese Bindung erst zur Laufzeit im Browser auf, sodass das Remote-Team eine neue Version veröffentlichen kann, und die Host-Anwendung lädt beim nächsten Seitenaufruf automatisch die aktuelle Version, ohne dass irgendjemand die Host-Anwendung neu bauen muss.
Diese Eigenschaft macht Module Federation zu einer der populärsten Techniken für Micro-Frontend-Architekturen mit Vue, weil sie echte Deployment-Unabhängigkeit mit dem Komfort nativer JavaScript-Module verbindet. Der Preis dafür ist zusätzliche Konfigurationskomplexität rund um geteilte Abhängigkeiten, Versionskompatibilität und Fehlerbehandlung, wenn ein Remote zur Laufzeit nicht erreichbar ist.
2. Host und Remote: die Grundkonfiguration
Jede Module Federation Konfiguration unterscheidet zwischen Host und Remote. Der Host ist die Anwendung, die fremde Module konsumiert, der Remote ist die Anwendung, die eigene Module über einen Entry-Point namens remoteEntry.js zur Verfügung stellt. Eine Vue-Anwendung kann beide Rollen gleichzeitig einnehmen: Sie konsumiert Module von einem Team und stellt selbst Module für ein anderes Team bereit.
Die Grundkonfiguration definiert auf Remote-Seite, welche Module unter welchem Namen exponiert werden, und auf Host-Seite, unter welcher URL welches Remote erreichbar ist. Diese URL zeigt bei Module Federation in Vue-Apps typischerweise auf eine CDN-Adresse oder einen Storage-Bucket, in den die CI/CD-Pipeline des Remote-Teams bei jedem Deployment eine neue Version hochlädt.
// remote/vite.config.js — Exposes Vue components as federated modules
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import federation from '@originjs/vite-plugin-federation';
export default defineConfig({
plugins: [
vue(),
federation({
name: 'checkout_remote',
filename: 'remoteEntry.js',
exposes: {
'./CheckoutSummary': './src/components/CheckoutSummary.vue',
'./useCheckoutState': './src/composables/useCheckoutState.js',
},
shared: ['vue', 'vue-router', 'pinia'],
}),
],
build: {
target: 'esnext',
modulePreload: false,
cssCodeSplit: false,
},
});
// host/vite.config.js — Consumes the remote module
export default defineConfig({
plugins: [
vue(),
federation({
name: 'shell_host',
remotes: {
checkout_remote: 'https://cdn.mironsoft.de/checkout/remoteEntry.js',
},
shared: ['vue', 'vue-router', 'pinia'],
}),
],
});
3. Exposes: Vue-Komponenten gezielt freigeben
Der exposes-Block entscheidet, welche Teile eines Vue-Moduls überhaupt für andere Anwendungen sichtbar sind. Eine bewährte Praxis bei Module Federation mit Vue ist es, nicht die komplette Anwendung, sondern gezielt einzelne, klar abgegrenzte Komponenten und Composables freizugeben. Das hält die öffentliche Schnittstelle des Remote-Moduls klein und stabil, während interne Implementierungsdetails, etwa Hilfskomponenten, die nur intern verwendet werden, gar nicht erst exportiert werden.
Beim Import auf Host-Seite wird das exponierte Modul über einen dynamischen Import geladen, dessen Modulname sich aus dem Remote-Namen und dem exponierten Pfad zusammensetzt. Vue behandelt die geladene Komponente danach wie jede andere asynchrone Komponente, inklusive der Möglichkeit, sie mit defineAsyncComponent und einem Ladezustand zu kombinieren, während der Netzwerk-Request für den Remote-Chunk noch läuft.
// host/src/components/CheckoutSlot.vue — Consuming a remote Vue component
import { defineAsyncComponent } from 'vue';
const RemoteCheckoutSummary = defineAsyncComponent({
loader: () => import('checkout_remote/CheckoutSummary'),
loadingComponent: CheckoutSkeleton,
errorComponent: CheckoutFallback,
delay: 100,
timeout: 5000,
});
export default {
components: { RemoteCheckoutSummary },
template: `<RemoteCheckoutSummary :order-id="orderId" />`,
props: ['orderId'],
};
4. Shared Dependencies richtig konfigurieren
Ohne explizite Konfiguration würde jedes Remote seine eigene Kopie von Vue mitbringen, was zu mehrfach geladenem Framework-Code und, schlimmer noch, zu Reaktivitätsfehlern führt, weil zwei verschiedene Vue-Instanzen nicht dieselben Komponenten-Instanzen und Reaktivitätsgraphen teilen können. Der shared-Block in Module Federation löst dieses Problem, indem er definiert, welche Bibliotheken zwischen Host und Remote geteilt werden, statt sie mehrfach zu laden.
Für Vue-Apps sind mindestens vue, vue-router und, falls verwendet, pinia Kandidaten für die Shared-Konfiguration. Wichtig ist die Einstellung singleton: true, die erzwingt, dass zur Laufzeit tatsächlich nur eine einzige Instanz existiert, statt mehrere kompatible Versionen parallel zu laden. Ohne singleton kann es vorkommen, dass Host und Remote unterschiedliche Vue-Instanzen verwenden, was zu subtilen Fehlern bei Provide/Inject oder globalen Plugins führt.
// vite.config.js — Explicit shared dependency configuration with version constraints
federation({
name: 'shell_host',
remotes: {
checkout_remote: 'https://cdn.mironsoft.de/checkout/remoteEntry.js',
},
shared: {
vue: {
singleton: true, // exactly one Vue instance across host and remote
requiredVersion: '^3.4.0',
strictVersion: false, // warn instead of hard-fail on mismatch
},
'vue-router': {
singleton: true,
requiredVersion: '^4.3.0',
},
pinia: {
singleton: true,
requiredVersion: '^2.1.0',
},
},
});
5. Versionierung und Kompatibilitätsverträge
Weil Host und Remote unabhängig voneinander deployt werden, entsteht ein organisatorisches Problem, das rein technisch nicht vollständig lösbar ist: Wer garantiert, dass ein Remote-Team seine exponierte Vue-Komponente nicht auf eine Weise ändert, die den Host bricht? Module Federation in Vue-Apps braucht deshalb einen expliziten Kompatibilitätsvertrag zwischen den Teams, typischerweise in Form von Contract-Tests, die vor jedem Remote-Deployment gegen die aktuelle Host-Integration laufen.
Ein pragmatischer Ansatz ist Semantic Versioning für die exponierten Module: Ein Breaking Change an einer exponierten Vue-Komponente, etwa eine geänderte Prop-Signatur, erfordert einen neuen Major-Pfad im Manifest, sodass alte Host-Versionen weiterhin die alte Remote-Version laden können, während neue Host-Versionen bewusst auf die neue Remote-Version migrieren. Ohne diese Disziplin driften Host und Remote unbemerkt auseinander, bis ein Produktionsfehler die Inkompatibilität sichtbar macht.
#!/usr/bin/env bash
# ci/contract-test-remote.sh — Run before every remote deployment
set -euo pipefail
readonly HOST_STAGING_URL="https://staging.mironsoft.de"
readonly REMOTE_BUILD_DIR="dist"
# Serve the newly built remote locally and point a real host build at it
npx serve "$REMOTE_BUILD_DIR" --listen 4174 &
SERVER_PID=$!
trap 'kill $SERVER_PID' EXIT
# Contract tests run the host's integration test suite against this remote build
npm run test:contract -- --remote-url=http://localhost:4174/remoteEntry.js
echo "[OK] Remote is compatible with the current host integration contract"
6. Fallback-Strategien bei nicht erreichbaren Remotes
Ein Remote, das zur Laufzeit nicht erreichbar ist, etwa wegen eines CDN-Ausfalls oder eines fehlerhaften Deployments, darf niemals die gesamte Host-Anwendung zum Absturz bringen. Module Federation mit Vue erfordert deshalb eine explizite Fehlerbehandlung auf Host-Seite: errorComponent bei defineAsyncComponent, kombiniert mit einem Timeout, damit ein hängender Netzwerk-Request nicht die gesamte Seite blockiert.
Für kritische Remote-Module, etwa den Checkout, empfiehlt sich zusätzlich eine lokal gebündelte Fallback-Version innerhalb des Hosts, die nur dann aktiviert wird, wenn das Remote nicht innerhalb des Timeouts antwortet. Diese Fallback-Version muss nicht funktional vollständig sein, sollte aber zumindest den kritischen Pfad, etwa das Abschließen einer Bestellung, in einer einfacheren Form abdecken, statt dem Nutzer eine leere Seite oder eine Fehlermeldung zu zeigen.
7. TypeScript-Typen über Remote-Grenzen hinweg
TypeScript-Typsicherheit über Module Federation Grenzen hinweg ist eine der größten Reibungsflächen in der Praxis, weil der Host zur Build-Zeit gar nicht weiß, welche Props eine Remote-Komponente erwartet. Die verbreitete Lösung ist, dass jedes Remote-Team eine separate .d.ts-Datei mit den öffentlichen Typen seiner exponierten Module veröffentlicht, entweder als kleines npm-Paket oder als generierte Datei, die im CI-Prozess automatisch aus den Vue-Komponenten extrahiert wird.
Diese Typdeklarationen sind rein statisch, sie werden zur Entwicklungszeit im Host eingebunden, verändern aber nicht das Laufzeitverhalten von Module Federation. Das bedeutet: Ein Host-Entwickler bekommt beim Import einer Remote-Vue-Komponente vollständige Autovervollständigung und Typprüfung in der IDE, während die tatsächliche Komponente weiterhin dynamisch zur Laufzeit geladen wird. Ohne diese Typdeklarationen degradiert jede Remote-Komponente im Host zu any, was Tippfehler bei Props erst zur Laufzeit sichtbar macht.
8. Performance: Preloading und Bundle-Größe
Ein häufiges Missverständnis bei Module Federation in Vue-Apps ist, dass geteilte Abhängigkeiten automatisch Performance-Probleme lösen. Tatsächlich muss der Browser bei jedem ersten Zugriff auf ein Remote-Modul einen zusätzlichen Netzwerk-Request auslösen, selbst wenn Vue selbst bereits geladen ist. Für Module, die auf jeder Seite benötigt werden, etwa ein Header oder eine Navigation, lohnt sich deshalb Preloading über <link rel="modulepreload">, um den zusätzlichen Request bereits während des initialen Seitenaufbaus zu starten.
Die Bundle-Größe jedes Remotes sollte separat überwacht werden, weil ein einzelnes, unbedacht wachsendes Remote-Modul die gefühlte Ladezeit der gesamten Host-Anwendung verschlechtern kann, ohne dass dies im Host-Bundle selbst sichtbar wird. Bundle-Analyse-Tools wie rollup-plugin-visualizer sollten deshalb in jeder Remote-Pipeline laufen, nicht nur im Host, um Regressionen frühzeitig zu erkennen, bevor sie in Produktion Nutzer betreffen.
// host/src/preload.js — Preloading frequently needed remotes during initial page load
export function preloadRemote(remoteEntryUrl) {
const link = document.createElement('link');
link.rel = 'modulepreload';
link.href = remoteEntryUrl;
document.head.appendChild(link);
}
// Called for remotes needed on nearly every page, e.g. header or navigation
preloadRemote('https://cdn.mironsoft.de/header/remoteEntry.js');
// Bundle size budget enforced in the remote's own CI pipeline
// rollup-plugin-visualizer + a size-limit check, not just in the host
9. Module Federation im Werkzeugvergleich
Neben vite-plugin-federation und dem nativen Webpack-5-Federation gibt es weitere Ansätze für Runtime-Integration bei Vue-Apps, die unterschiedliche Kompromisse zwischen Reifegrad, Bundler-Bindung und Funktionsumfang eingehen.
| Werkzeug | Bundler-Bindung | Reifegrad | Besonderheit |
|---|---|---|---|
| Webpack 5 Module Federation | Webpack | Sehr hoch | Referenzimplementierung, größtes Ökosystem |
| vite-plugin-federation | Vite | Gut | Schnellere Dev-Builds, jüngeres Projekt |
| Module Federation 2.0 | Bundler-agnostisch | Wachsend | Runtime-API, Typen-Sync eingebaut |
| Native Federation (Angular-Team) | Keine | Gut | Basiert auf Import Maps, framework-agnostisch |
| Custom Elements (Alternative) | Keine | Sehr hoch | Kein geteiltes Vue nötig, dafür Shadow-DOM-Isolation |
Für neue Vue-Projekte, die bereits Vite als Build-Tool verwenden, ist vite-plugin-federation meist die pragmatischste Wahl, weil es sich nahtlos in die bestehende Vite-Konfiguration einfügt. Projekte, die bereits Webpack einsetzen oder maximale Reife und Community-Support brauchen, profitieren stärker von der Webpack-5-Referenzimplementierung.
Mironsoft
Module Federation, Micro Frontends und Vue-Runtime-Architektur
Vue-Module zur Laufzeit sicher zusammenführen?
Wir konfigurieren Module Federation für eure Vue-Apps, definieren Shared-Dependency-Verträge zwischen Teams und bauen Fallback-Strategien, die auch bei einem gestörten Remote stabil bleiben.
Setup
Host- und Remote-Konfiguration mit Vite oder Webpack produktionsreif aufsetzen
Versionsverträge
Contract-Tests und Kompatibilitätsregeln zwischen Teams etablieren
Resilienz
Fallback-Komponenten und Timeout-Strategien für ausgefallene Remotes
10. Zusammenfassung
Module Federation in Vue-Apps löst Runtime-Integration zwischen unabhängig deployten Anwendungen, indem sie JavaScript-Module erst im Browser zusammenführt, statt sie zur Build-Zeit fest zu verdrahten. Die Konfiguration von Host, Remote und exponierten Modulen ist der einfache Teil. Die eigentliche Herausforderung liegt in geteilten Abhängigkeiten mit singleton: true, expliziten Versionsverträgen zwischen Teams und robusten Fallback-Strategien für den Fall, dass ein Remote nicht erreichbar ist.
TypeScript-Typsicherheit über Remote-Grenzen hinweg erfordert zusätzliche Tooling-Disziplin, etwa veröffentlichte Typdeklarationen pro Remote. Performance-seitig lohnt sich Preloading für häufig benötigte Module und separate Bundle-Analyse pro Remote. Wer diese Bausteine sauber umsetzt, bekommt mit Module Federation eine der flexibelsten Techniken für skalierbare Vue-Architekturen mit mehreren unabhängigen Teams.
Module Federation in Vue-Apps — Das Wichtigste auf einen Blick
Host & Remote
Host konsumiert, Remote exponiert über remoteEntry.js. Beide Rollen sind kombinierbar.
Shared Dependencies
singleton: true für Vue, Router und Pinia verhindert doppelt geladene Instanzen.
Versionsverträge
Contract-Tests zwischen Teams verhindern Breaking Changes an exponierten Modulen.
Resilienz
errorComponent, Timeout und lokale Fallback-Version für ausgefallene Remotes.