Nahtlose Vereinigung diverser Frontend-Anwendungen mit Modul-Föderation
Ethan Miller
Product Engineer · Leapcell

Einleitung
In der sich schnell entwickelnden Landschaft der modernen Webentwicklung, insbesondere in großen Organisationen oder komplexen Produktsuiten, stellt die monolithische Frontend-Architektur oft erhebliche Herausforderungen dar. Teams kämpfen mit langsamen Build-Zeiten, Schwierigkeiten bei der Bereitstellung unabhängiger Funktionen und dem allgegenwärtigen Risiko eng gekoppelter Bereitstellungen. Wenn die Komplexität und der Funktionsumfang von Anwendungen wachsen, wird die Notwendigkeit eines modulareren, skalierbareren und unabhängig bereitstellbaren Ansatzes immer wichtiger. Hier kommt das Konzept der Zusammensetzung mehrerer unabhängiger Frontend-Anwendungen zu einer einzigen, einheitlichen Benutzererfahrung ins Spiel. Dieser Artikel befasst sich mit der Modul-Föderation, einer leistungsstarken Funktion, die genau diese Herausforderungen bewältigt und es Entwicklern ermöglicht, hochentwickelte Systeme durch nahtlose Integration unterschiedlicher Frontend-Anwendungen zu erstellen.
Kernkonzepte der Modul-Föderation verstehen
Bevor wir uns mit der Mechanik und den Vorteilen der Modul-Föderation befassen, ist es wichtig, einige Kernkonzepte zu verstehen, die ihrer Funktionsweise zugrunde liegen.
- Host- (oder Container-)Anwendung: Dies ist die Hauptanwendung, die Module von anderen Remote-Anwendungen verbraucht und orchestriert. Sie fungiert als Hülle oder Wrapper.
- Remote-Anwendung: Dies sind die unabhängigen Anwendungen, die bestimmte Module oder Komponenten für den Verbrauch durch andere Host- oder Remote-Anwendungen bereitstellen.
- Exponierte Module: Dies sind die spezifischen Codefragmente (Komponenten, Funktionen, Dienstprogramme usw.), die eine Remote-Anwendung öffentlich zur Nutzung durch andere Anwendungen zur Verfügung stellen möchte.
- Gemeinsame Module: Modul-Föderation unterstützt inhärent das Teilen von Abhängigkeiten zwischen Anwendungen. Dies ist entscheidend für die Optimierung der Bundle-Größen und stellt sicher, dass nur eine Version einer gemeinsam genutzten Bibliothek (wie React oder Vue) zur Laufzeit geladen wird, wodurch potenzielle Konflikte und Bloat verhindert werden.
Wie Modul-Föderation funktioniert
Die mit Webpack 5 eingeführte Modul-Föderation ermöglicht es einer JavaScript-Anwendung, Code aus einer anderen Anwendung dynamisch zu laden und Abhängigkeiten auf robuste und effiziente Weise zu teilen. Im Kern ermöglicht die Modul-Föderation einer „Host“-Anwendung, „Remote“-Module zur Laufzeit zu laden.
Stellen Sie sich eine E-Commerce-Plattform vor. Anstatt die gesamte Plattform als eine massive Frontend-Anwendung zu erstellen, könnten Sie die Zuständigkeiten in unabhängige Micro-Frontends aufteilen: eine „Product Catalog“-Anwendung, eine „Shopping Cart“-Anwendung und eine „User Profile“-Anwendung. Mit Modul-Föderation könnte der „Product Catalog“ eine FeaturedProduct
-Komponente bereitstellen, der „Shopping Cart“ einen AddToCartButton
und das „User Profile“ ein AuthWidget
. Eine Haupt-„Shell“-Anwendung fungiert dann als Host und ruft diese Komponenten bei Bedarf ab und rendert sie.
Lassen Sie uns dies anhand eines einfachen Beispiels veranschaulichen.
Einrichten einer Remote-Anwendung (Komponenten bereitstellen)
Zuerst definieren wir unsere Remote-Anwendung, nennen wir sie product-catalog
. Ihr Ziel ist es, eine ProductCard
-Komponente bereitzustellen.
// product-catalog/src/components/ProductCard.jsx import React from 'react'; const ProductCard = ({ name, price }) => { return ( <div style={{ border: '1px solid #ccc', padding: '10px', margin: '10px' }}> <h3>{name}</h3> <p>Price: ${price}</p> <button>View Details</button> </div> ); }; export default ProductCard;
Jetzt konfigurieren wir das webpack.config.js
für product-catalog
, um diese Komponente bereitzustellen:
// product-catalog/webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); const { ModuleFederationPlugin } = require('webpack').container; module.exports = { mode: 'development', entry: './src/index.js', output: { publicPath: 'http://localhost:3001/', // Public path for the exposed modules }, devServer: { port: 3001, }, module: { rules: [ { test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules/, options: { presets: ['@babel/preset-react'], }, }, ], }, plugins: [ new ModuleFederationPlugin({ name: 'product_catalog', // Unique name for this remote application filename: 'remoteEntry.js', // The file that contains exposed modules metadata exposes: { './ProductCard': './src/components/ProductCard', // Expose ProductCard component }, shared: { react: { singleton: true, requiredVersion: '^18.0.0' }, 'react-dom': { singleton: true, requiredVersion: '^18.0.0' }, }, }), new HtmlWebpackPlugin({ template: './public/index.html', }), ], };
Hier ist name
der eindeutige Bezeichner für dieses Remote. filename
ist die Manifest-Datei, die der Host abrufen wird. exposes
definiert, welche Module verfügbar sind. shared
ist entscheidend für die Deduplizierung von Abhängigkeiten.
Einrichten einer Host-Anwendung (Komponenten konsumieren)
Als Nächstes erstellen wir unsere Host-Anwendung main-shell
, die ProductCard
von product-catalog
konsumieren wird.
// main-shell/src/App.jsx import React, { Suspense } from 'react'; // Dynamisch die Remote-Komponente importieren const ProductCard = React.lazy(() => import('product_catalog/ProductCard')); const App = () => { return ( <div> <h1>Willkommen im Main Shell!</h1> <h2>Featured Products:</h2> <Suspense fallback={<div>Loading Product Card...</div>}> <ProductCard name="Stylish Headphones" price="199.99" /> <ProductCard name="Ergonomic Keyboard" price="120.00" /> </Suspense> </div> ); }; export default App;
Und seine webpack.config.js
:
// main-shell/webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); const { ModuleFederationPlugin } = require('webpack').container; module.exports = { mode: 'development', entry: './src/index.js', output: { publicPath: 'http://localhost:3000/', }, devServer: { port: 3000, }, module: { rules: [ { test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules/, options: { presets: ['@babel/preset-react'], }, }, ], }, plugins: [ new ModuleFederationPlugin({ name: 'main_shell', remotes: { product_catalog: 'product_catalog@http://localhost:3001/remoteEntry.js', // Define the remote application }, shared: { react: { singleton: true, requiredVersion: '^18.0.0' }, 'react-dom': { singleton: true, requiredVersion: '^18.0.0' }, }, }), new HtmlWebpackPlugin({ template: './public/index.html', }), ], };
Das Feld remotes
in der Konfiguration des Hosts teilt Webpack mit, wo die remoteEntry.js
-Datei von product_catalog
zu finden ist. Die shared
-Konfiguration hier spiegelt die der Remote wider und stellt sicher, dass react
und react-dom
Singleton-Instanzen sind.
Wenn die Anwendung main-shell
geladen wird, ruft sie dynamisch die remoteEntry.js
von product_catalog
ab (eine kleine Manifest-Datei), die ihr mitteilt, wie sie die ProductCard
-Komponente erhält. Dies ermöglicht die Laufzeitintegration physisch getrennter Anwendungen.
Anwendungsszenarien
Modul-Föderation eignet sich hervorragend für Szenarien, in denen Sie:
- Monolithische Frontends in Micro-Frontends aufteilen: Jedes Micro-Frontend kann unabhängig von verschiedenen Teams entwickelt, bereitgestellt und skaliert werden.
- Gemeinsame Komponenten oder Bibliotheken teilen: Vermeiden Sie Code-Duplizierung, indem Sie UI-Komponentenbibliotheken, Hilfsfunktionen oder sogar ganze Geschäftslogikmodule über mehrere Anwendungen hinweg teilen.
- A/B-Tests oder Feature Flags implementieren: Laden Sie dynamisch verschiedene Versionen von Komponenten oder Funktionen basierend auf Benutzergruppen oder experimentellen Bedingungen, ohne die gesamte Anwendung neu bereitzustellen.
- Erweiterbare Plattformen erstellen: Ermöglichen Sie Drittanbieterentwicklern oder anderen internen Teams, die Kernplattform zu erweitern, indem sie ihre eigenen Module bereitstellen, die über föderierte Verbindungen eingebunden werden können.
Fazit
Modul-Föderation stellt einen Paradigmenwechsel in der Art und Weise dar, wie wir die groß angelegte Frontend-Entwicklung angehen. Indem sie Entwicklern die Möglichkeit gibt, mehrere unabhängige Anwendungen zu einer einzigen, kohärenten Erfahrung zusammenzustellen, verbessert sie die Skalierbarkeit, vereinfacht die Bereitstellung und fördert die Code-Wiederverwendung über verschiedene Teams und Projekte hinweg. Diese leistungsstarke Webpack-Funktion ermöglicht wirklich eine modulare Zukunft für Webanwendungen und bringt uns der Verheißung wirklich unabhängiger und robuster Micro-Frontend-Architekturen näher.