Nahtlose Integration von React- und Vue-Anwendungen in Astro unter Verwendung von Module Federation
Grace Collins
Solutions Engineer · Leapcell

Einleitung
In der sich rasant entwickelnden Landschaft der Frontend-Entwicklung beinhaltet der Aufbau großer Anwendungen oft, dass mehrere Teams an verschiedenen Teilen eines Projekts arbeiten und potenziell verschiedene Frameworks verwenden. Dieses Szenario führt häufig zu der Herausforderung, diese unterschiedlichen Teile zu einer kohärenten Benutzererfahrung zu integrieren, ohne Leistung oder Wartbarkeit zu beeinträchtigen. Herkömmliche Ansätze wie IFrames oder serverseitige Includes stoßen an Grenzen in Bezug auf Kommunikation, Styling und das Teilen des Anwendungskontextes. Das Aufkommen von Meta-Frameworks wie Astro, die auf optimale Leistung durch Inselarchitektur ausgelegt sind, verstärkt den Bedarf an effektiven Integrationsstrategien. Dieser Artikel untersucht, wie Module Federation eine elegante Lösung für genau dieses Problem bietet, indem es uns ermöglicht, unabhängige React- und Vue-Anwendungen nahtlos in ein Astro-Projekt zu integrieren, wodurch größere Flexibilität erschlossen und eine wirklich zusammensetzbare Frontend-Architektur gefördert wird.
Kernkonzepte erklärt
Bevor wir uns mit den Implementierungsdetails befassen, lassen Sie uns einige Schlüsselbegriffe klären, die für unsere Diskussion zentral sind:
-
Module Federation: Eine Webpack-Funktion, die es einer JavaScript-Anwendung ermöglicht, Code dynamisch von einer anderen Anwendung zu laden und umgekehrt. Sie ermöglicht das Teilen und Integrieren von Codebasen über verschiedene Anwendungen hinweg, die oft als „Remotes“ (die Module bereitstellenden Anwendungen) und „Hosts“ (die sie konsumierenden Anwendungen) bezeichnet werden. Die Kerninnovation besteht darin, ganze Anwendungen oder Komponenten als Module zu behandeln, die zur Laufzeit gemeinsam genutzt und konsumiert werden können.
-
Micro-Frontends: Ein architektonischer Stil, bei dem eine Webanwendung aus unabhängigen Frontend-Anwendungen besteht, die jeweils von einem separaten Team gepflegt werden. Module Federation ist ein leistungsfähiges Werkzeug zur Realisierung von Micro-Frontend-Architekturen.
-
Astro: Ein moderner Static-Site-Builder/Meta-Framework, das auf Geschwindigkeit ausgelegt ist. Es verwendet eine „Inselarchitektur“, bei der der Großteil des JavaScripts aus dem endgültigen Build entfernt wird, und nur interaktive UI-Komponenten (Inseln) bei Bedarf mit JavaScript gehydriert werden. Dieser Ansatz funktioniert typischerweise mit verschiedenen UI-Frameworks wie React, Vue, Svelte usw.
-
Host-Anwendung: Im Kontext der Module Federation ist dies die Anwendung, die Module von anderen Anwendungen konsumiert. In unserem Fall fungiert das Astro-Projekt als Host.
-
Remote-Anwendung: Eine Anwendung, die Module zur Konsumtion durch andere Anwendungen bereitstellt. Hier dienen unsere unabhängigen React- und Vue-Anwendungen als Remotes.
Prinzip und Implementierung
Das Kernprinzip hinter dieser Integration ist einfach: Wir konfigurieren unsere unabhängigen React- und Vue-Anwendungen so, dass sie als Module Federation Remotes fungieren und ihre Komponenten bereitstellen. Unser Astro-Projekt, das als Host fungiert, konsumiert dann diese bereitgestellten Komponenten. Astros inhärente Fähigkeit, verschiedene UI-Frameworks zu unterstützen, bedeutet, dass es diese über Module Federation importierten React- und Vue-Komponenten genauso rendern kann wie lokale Komponenten.
Remote-Anwendungen einrichten (React und Vue)
Zuerst bereiten wir unsere React- und Vue-Anwendungen darauf vor, Komponenten bereitzustellen. Wir verwenden das Module Federation Plugin von Webpack 5.
Beispiel für eine React-Remote-Anwendung
Nehmen wir an, wir haben eine einfache React-App, die eine Counter
-Komponente bereitstellen möchte.
react-remote/webpack.config.js
:
const HtmlWebPackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); const deps = require('./package.json').dependencies; module.exports = { output: { publicPath: 'http://localhost:3001/', // Dies ist entscheidend, damit Astro das Remote finden kann }, resolve: { extensions: ['.jsx', '.js', '.json'], }, devServer: { port: 3001, }, module: { rules: [ { test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules/, options: { presets: ['@babel/preset-react'], }, }, ], }, plugins: [ new ModuleFederationPlugin({ name: 'react_remote', filename: 'remoteEntry.js', exposes: { './CounterApp': './src/CounterApp', // Stellen Sie Ihre Hauptkomponente bereit }, shared: { ...deps, react: { singleton: true, requiredVersion: deps.react, }, 'react-dom': { singleton: true, requiredVersion: deps['react-dom'], }, }, }), new HtmlWebPackPlugin({ template: './public/index.html', }), ], };
react-remote/src/CounterApp.jsx
:
import React, { useState } from 'react'; const CounterApp = () => { const [count, setCount] = useState(0); return ( <div style={{ padding: '10px', border: '1px solid blue' }}> <h2>React Counter Component</h2> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; export default CounterApp;
Beispiel für eine Vue-Remote-Anwendung
Ähnlich kann eine Vue-App eine Greeting
-Komponente bereitstellen.
vue-remote/webpack.config.js
:
const { VueLoaderPlugin } = require('vue-loader'); const HtmlWebPackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); const deps = require('./package.json').dependencies; module.exports = { output: { publicPath: 'http://localhost:3002/', // Wichtig für Astro }, resolve: { extensions: ['.vue', '.js', '.json'], }, devServer: { port: 3002, }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/, }, { test: /\.css$/, use: ['style-loader', 'css-loader'], }, ], }, plugins: [ new VueLoaderPlugin(), new ModuleFederationPlugin({ name: 'vue_remote', filename: 'remoteEntry.js', exposes: { './GreetingApp': './src/GreetingApp.vue', // Stellen Sie Ihre Hauptkomponente bereit }, shared: { ...deps, vue: { singleton: true, requiredVersion: deps.vue, }, }, }), new HtmlWebPackPlugin({ template: './public/index.html', }), ], };
vue-remote/src/GreetingApp.vue
:
<template> <div style="padding: 10px; border: 1px solid green;"> <h2>Vue Greeting Component</h2> <p>Hello from Vue!</p> <button @vue-click="greet">Click Me</button> </div> </template> <script> export default { methods: { greet() { alert('Vue says hi!'); } } } </script> <style scoped> /* Scoped styles specific to this component */ </style>
Stellen Sie sicher, dass beide Remote-Anwendungen laufen (z.B. npm start
in jedem Verzeichnis).
Konsumieren in Astro (Host-Anwendung)
Nun konfigurieren wir unser Astro-Projekt so, dass es diese Remotes konsumiert. Astros src/pages
oder src/components
ist der Ort, an dem wir unsere Remote-Komponenten importieren und rendern. Astro verwendet typischerweise Vite, daher benötigen wir eine Möglichkeit, Vite mitzuteilen, wie Module Federation behandelt werden soll. Obwohl Vite Module Federation nicht nativ wie Webpack unterstützt, gibt es etablierte Plugins, die diese Lücke schließen. Wir verwenden vite-plugin-federation
.
astro-host/vite.config.ts
:
import { defineConfig } from 'vite'; import { svelte } from '@sveltejs/vite-plugin-svelte'; // Beispiel für ein anderes Framework, nicht direkt für MF erforderlich import react from '@vitejs/plugin-react'; import vue from '@vitejs/plugin-vue'; import federation from '@module-federation/vite'; export default defineConfig({ plugins: [ react(), vue(), federation({ name: 'astro_host', remotes: { react_remote: 'http://localhost:3001/remoteEntry.js', vue_remote: 'http://localhost:3002/remoteEntry.js', }, shared: ['react', 'react-dom', 'vue'], // Stellen Sie gemeinsame Abhängigkeiten sicher. Astro verwendet diese möglicherweise nicht direkt, aber sie sind für Remote-Anwendungen wichtig. }), ], });
astro-host/astro.config.mjs
:
import { defineConfig } from 'astro/config'; import react from '@astrojs/react'; import vue from '@astrojs/vue'; // https://astro.build/config export default defineConfig({ integrations: [react(), vue()], vite: { plugins: [ // Stellen Sie sicher, dass das Federation-Plugin in vite.config.ts ordnungsgemäß verknüpft ist // Oder duplizieren Sie die Federation-Konfiguration hier, wenn nicht alle Vite-Konfigurationen in vite.config.ts erfolgen // Der Einfachheit halber gehen wir davon aus, dass vite.config.ts von Astro übernommen wird. // Wenn nicht, müssen Sie möglicherweise die `federation` hier manuell importieren und konfigurieren. ], }, });
Nun erstellen wir Astro-Komponenten, die diese föderierten Komponenten importieren und rendern.
astro-host/src/pages/index.astro
:
--- import Layout from '../layouts/Layout.astro'; import ReactCounter from '../components/ReactCounter.astro'; import VueGreeting from '../components/VueGreeting.astro'; --- <Layout title="Module Federation in Astro"> <main> <h1>Willkommen bei Astro mit Module Federation!</h1> <p>Diese Seite integriert Komponenten aus unabhängigen React- und Vue-Anwendungen.</p> <div style="margin-top: 20px;"> <h2>In Astro gehostete React-Komponente</h2> <ReactCounter client:load /> {/* client:load hydriert die React-Komponente */} </div> <div style="margin-top: 20px;"> <h2>In Astro gehostete Vue-Komponente</h2> <VueGreeting client:load /> {/* client:load hydriert die Vue-Komponente */} </div> </main> </Layout> <style> main { margin: auto; padding: 1.5rem; max-width: 60ch; } h1 { font-size: 3rem; font-weight: 800; margin: 0; } </style>
astro-host/src/components/ReactCounter.astro
:
--- import { createRemoteComponent } from '../lib/module-federation'; const RemoteCounter = await createRemoteComponent('react_remote', './CounterApp'); --- <RemoteCounter client:load />
astro-host/src/components/VueGreeting.astro
:
--- import { createRemoteComponent } from '../lib/module-federation'; const RemoteGreeting = await createRemoteComponent('vue_remote', './GreetingApp'); --- <RemoteGreeting client:load />
astro-host/src/lib/module-federation.ts
: (Dienstprogramm zum dynamischen Laden von Remote-Komponenten)
import { createComponent } from '@module-federation/sdk'; import type { AstroComponentFactory } from 'astro/runtime/server/index.js'; export async function createRemoteComponent(remoteName: string, moduleName: string): Promise<AstroComponentFactory> { const Component = await createComponent({ remote: remoteName, module: moduleName, scope: remoteName, // Scope stimmt normalerweise mit dem Remote-Namen überein // Wichtig: Für Astro müssen Sie sicherstellen, dass die Remote-Komponente als clientseitige Insel behandelt wird // Der Framework-Adapter (z.B. @astrojs/react, @astrojs/vue) kümmert sich um die eigentliche Hydrierung }); // Diese Konvertierung kann je nach genauer Version und Verwendung von `@module-federation/sdk` und Astros internem Komponentenhandling variieren. // Das Ziel ist, eine Astro-kompatible Komponente zu erhalten. // Für die meisten Frameworks kann die importierte Komponente direkt verpackt werden, um mit der client: Direktive verwendet zu werden. return Component as AstroComponentFactory; }
Hinweis: Die Implementierung von createRemoteComponent
muss möglicherweise je nach genauer Version und Verwendung von @module-federation/sdk
und Astros internem Komponentenhandling angepasst werden. Die grundlegende Idee besteht darin, das Remote-Modul dynamisch zu importieren und es dann innerhalb von Astro zu rendern. Die client:load
-Direktive ist entscheidend dafür, dass Astro diese Komponenten auf der Client-Seite hydriert und sie interaktiv macht.
Anwendungsszenarien
- Micro-Frontend-Architekturen: Jede unabhängige Anwendung (React, Vue) kann unabhängig entwickelt und bereitgestellt werden und unter der Verantwortung verschiedener Teams liegen. Astro fungiert als Orchestrierungsebene und fügt sie zu einem einzigen Produkt zusammen.
- Schrittweise Migration: Bei der Migration einer großen Anwendung von einem Framework zu einem anderen können Sie schrittweise Teile der Anwendung als neue Micro-Frontends neu schreiben und sie in die bestehende Shell (hier Astro) integrieren.
- Gemeinsame Komponentenbibliotheken: Teams können gemeinsame UI-Komponenten oder sogar Geschäftslogikmodule als föderierte Module bereitstellen, die es anderen Remote- oder Host-Anwendungen ermöglichen, sie zu konsumieren, was Konsistenz und Wiederverwendbarkeit gewährleistet.
- Nutzung des Besten aus jedem Bereich: Ermöglicht verschiedenen Teams, das beste Framework für ihre spezifischen Funktionen zu wählen, ohne eine monolithische Framework-Entscheidung für das gesamte Projekt erzwingen zu müssen.
Fazit
Die Integration mehrerer unabhängiger React- und Vue-Anwendungen in einem Astro-Projekt unter Verwendung von Module Federation bietet einen leistungsstarken und flexiblen Ansatz für die moderne Frontend-Entwicklung. Durch die Ermöglichung des dynamischen Code-Sharings zur Laufzeit ermächtigt Module Federation Micro-Frontend-Architekturen, erleichtert schrittweise Migrationen und fördert die Wiederverwendbarkeit von Code über verschiedene Framework-Ökosysteme hinweg. Diese Strategie verbessert nicht nur das Entwicklererlebnis und stärkt die Teamautonomie, sondern liefert auch robuste, performante und wartbare Webanwendungen, indem sie Astros Geschwindigkeitsvorteile mit der Vielseitigkeit der frameworkübergreifenden Komponentenintegration nutzt. Die Zukunft der Frontend-Entwicklung ist zunehmend zusammensetzbar, und Module Federation ist ein Eckpfeiler dieses Paradigmas.