Odkryj moc mikro-frontendów dzięki JavaScript Module Federation w Webpack 5. Dowiedz się, jak budować skalowalne, łatwe w utrzymaniu i niezależne aplikacje internetowe.
JavaScript Module Federation z Webpack 5: Kompleksowy przewodnik po mikro-frontendach
W stale ewoluującym świecie tworzenia stron internetowych, budowanie dużych i złożonych aplikacji może być zniechęcającym zadaniem. Tradycyjne architektury monolityczne często prowadzą do wydłużonego czasu tworzenia, wąskich gardeł we wdrożeniach i wyzwań związanych z utrzymaniem jakości kodu. Mikro-frontendy pojawiły się jako potężny wzorzec architektoniczny, aby sprostać tym wyzwaniom, pozwalając zespołom budować i wdrażać niezależne części większej aplikacji internetowej. Jedną z najbardziej obiecujących technologii do implementacji mikro-frontendów jest JavaScript Module Federation, wprowadzona w Webpack 5.
Czym są mikro-frontendy?
Mikro-frontendy to styl architektoniczny, w którym aplikacja frontendowa jest rozłożona na mniejsze, niezależne jednostki, które mogą być rozwijane, testowane i wdrażane autonomicznie przez różne zespoły. Każdy mikro-frontend jest odpowiedzialny za określoną domenę biznesową lub funkcję, a wszystkie są komponowane razem w czasie rzeczywistym, tworząc kompletny interfejs użytkownika.
Pomyśl o tym jak o firmie: zamiast jednego gigantycznego zespołu deweloperskiego, masz wiele mniejszych zespołów skupiających się na konkretnych obszarach. Każdy zespół może pracować niezależnie, co pozwala na szybsze cykle rozwojowe i łatwiejsze utrzymanie. Weźmy za przykład dużą platformę e-commerce, taką jak Amazon; różne zespoły mogą zarządzać katalogiem produktów, koszykiem, procesem płatności i zarządzaniem kontem użytkownika. Wszystko to mogą być niezależne mikro-frontendy.
Zalety mikro-frontendów:
- Niezależne wdrożenia: Zespoły mogą wdrażać swoje mikro-frontendy niezależnie, bez wpływu na inne części aplikacji. Zmniejsza to ryzyko wdrożenia i pozwala na szybsze cykle wydań.
- Niezależność technologiczna: Różne mikro-frontendy mogą być budowane przy użyciu różnych technologii lub frameworków (np. React, Angular, Vue.js). Pozwala to zespołom na wybór najlepszej technologii dla ich specyficznych potrzeb i stopniowe wdrażanie nowych technologii bez konieczności przepisywania całej aplikacji. Wyobraź sobie jeden zespół używający Reacta do katalogu produktów, inny używający Vue.js do marketingowych stron docelowych, a trzeci używający Angulara do procesu płatności.
- Zwiększona autonomia zespołu: Zespoły mają pełną własność nad swoimi mikro-frontendami, co prowadzi do większej autonomii, szybszego podejmowania decyzji i poprawy produktywności deweloperów.
- Zwiększona skalowalność: Mikro-frontendy pozwalają na skalowanie aplikacji horyzontalnie poprzez wdrażanie poszczególnych mikro-frontendów na różnych serwerach.
- Współużytkowanie kodu: Współdzielone komponenty i biblioteki mogą być łatwo dzielone między mikro-frontendami.
- Łatwiejsze utrzymanie: Mniejsze bazy kodu są generalnie łatwiejsze do zrozumienia, utrzymania i debugowania.
Wyzwania mikro-frontendów:
- Zwiększona złożoność: Zarządzanie wieloma mikro-frontendami może zwiększyć złożoność ogólnej architektury, zwłaszcza w zakresie komunikacji, zarządzania stanem i wdrażania.
- Narzut wydajnościowy: Ładowanie wielu mikro-frontendów może wprowadzić narzut wydajnościowy, zwłaszcza jeśli nie są one odpowiednio zoptymalizowane.
- Problemy przekrojowe: Obsługa zagadnień przekrojowych, takich jak uwierzytelnianie, autoryzacja i motywy graficzne, może być wyzwaniem w architekturze mikro-frontendowej.
- Narzut operacyjny: Wymaga dojrzałych praktyk DevOps i infrastruktury do zarządzania wdrażaniem i monitorowaniem wielu mikro-frontendów.
Czym jest JavaScript Module Federation?
JavaScript Module Federation to funkcja Webpack 5, która pozwala na współdzielenie kodu między oddzielnie kompilowanymi aplikacjami JavaScript w czasie rzeczywistym. Umożliwia ona eksponowanie części aplikacji jako „modułów”, które mogą być konsumowane przez inne aplikacje, bez konieczności publikowania ich w centralnym repozytorium, takim jak npm.
Pomyśl o Module Federation jako o sposobie na stworzenie sfederowanego ekosystemu aplikacji, w którym każda aplikacja może wnosić własną funkcjonalność i konsumować funkcjonalność z innych aplikacji. Eliminuje to potrzebę zależności w czasie budowania i pozwala na prawdziwie niezależne wdrożenia.
Na przykład, zespół odpowiedzialny za system projektowy może eksponować komponenty interfejsu użytkownika jako moduły, a różne zespoły aplikacyjne mogą konsumować te komponenty bezpośrednio z aplikacji systemu projektowego, bez konieczności instalowania ich jako pakiety npm. Gdy zespół systemu projektowego zaktualizuje komponenty, zmiany są automatycznie odzwierciedlane we wszystkich konsumujących aplikacjach.
Kluczowe pojęcia w Module Federation:
- Host (Gospodarz): Główna aplikacja, która konsumuje zdalne moduły.
- Remote (Zdalny): Aplikacja, która eksponuje moduły do konsumpcji przez inne aplikacje.
- Shared Modules (Współdzielone moduły): Moduły, które są współdzielone między aplikacjami hosta i zdalnymi (np. React, Lodash). Module Federation może automatycznie zarządzać wersjonowaniem i deduplikacją współdzielonych modułów, aby zapewnić, że ładowana jest tylko jedna wersja każdego modułu.
- Exposed Modules (Wyeksponowane moduły): Konkretne moduły z aplikacji zdalnej, które są udostępniane do konsumpcji przez inne aplikacje.
- RemoteEntry.js: Plik generowany przez Webpack, który zawiera metadane o wyeksponowanych modułach aplikacji zdalnej. Aplikacja hosta używa tego pliku do odkrywania i ładowania zdalnych modułów.
Konfiguracja Module Federation z Webpack 5: Praktyczny przewodnik
Przejdźmy przez praktyczny przykład konfiguracji Module Federation z Webpack 5. Stworzymy dwie proste aplikacje: aplikację Host (Gospodarz) i aplikację Remote (Zdalną). Aplikacja Remote będzie eksponować komponent, a aplikacja Host będzie go konsumować.
1. Konfiguracja projektu
Utwórz dwa oddzielne katalogi dla swoich aplikacji: `host` i `remote`.
```bash mkdir host remote cd host npm init -y npm install webpack webpack-cli webpack-dev-server html-webpack-plugin --save-dev npm install react react-dom cd ../remote npm init -y npm install webpack webpack-cli webpack-dev-server html-webpack-plugin --save-dev npm install react react-dom ```2. Konfiguracja aplikacji zdalnej (Remote)
W katalogu `remote` utwórz następujące pliki:
- `src/index.js`: Punkt wejściowy aplikacji.
- `src/RemoteComponent.jsx`: Komponent, który będzie eksponowany.
- `webpack.config.js`: Plik konfiguracyjny Webpacka.
src/index.js:
```javascript import React from 'react'; import ReactDOM from 'react-dom/client'; import RemoteComponent from './RemoteComponent'; const App = () => (Remote Application
src/RemoteComponent.jsx:
```javascript import React from 'react'; const RemoteComponent = () => (This is a Remote Component!
Rendered from the Remote Application.
webpack.config.js:
```javascript const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); const path = require('path'); module.exports = { entry: './src/index', mode: 'development', devServer: { port: 3001, static: { directory: path.join(__dirname, 'dist'), }, }, output: { publicPath: 'auto', }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-env'], }, }, }, ], }, plugins: [ new ModuleFederationPlugin({ name: 'remote', filename: 'remoteEntry.js', exposes: { './RemoteComponent': './src/RemoteComponent', }, shared: { react: { singleton: true, eager: true }, 'react-dom': { singleton: true, eager: true }, }, }), new HtmlWebpackPlugin({ template: './public/index.html', }), ], resolve: { extensions: ['.js', '.jsx'], }, }; ```Utwórz plik `public/index.html` z podstawową strukturą HTML. Ważny jest element `
`3. Konfiguracja aplikacji hosta (Host)
W katalogu `host` utwórz następujące pliki:
- `src/index.js`: Punkt wejściowy aplikacji.
- `webpack.config.js`: Plik konfiguracyjny Webpacka.
src/index.js:
```javascript import React, { Suspense } from 'react'; import ReactDOM from 'react-dom/client'; const RemoteComponent = React.lazy(() => import('remote/RemoteComponent')); const App = () => (Host Application
webpack.config.js:
```javascript const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); const path = require('path'); module.exports = { entry: './src/index', mode: 'development', devServer: { port: 3000, static: { directory: path.join(__dirname, 'dist'), }, }, output: { publicPath: 'auto', }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-env'], }, }, }, ], }, plugins: [ new ModuleFederationPlugin({ name: 'host', remotes: { remote: 'remote@http://localhost:3001/remoteEntry.js', }, shared: { react: { singleton: true, eager: true }, 'react-dom': { singleton: true, eager: true }, }, }), new HtmlWebpackPlugin({ template: './public/index.html', }), ], resolve: { extensions: ['.js', '.jsx'], }, }; ```Utwórz plik `public/index.html` z podstawową strukturą HTML (podobną do aplikacji zdalnej). Ważny jest element `
`4. Instalacja Babel
W obu katalogach, `host` i `remote`, zainstaluj zależności Babel:
```bash npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader ```5. Uruchomienie aplikacji
W obu katalogach, `host` i `remote`, dodaj następujący skrypt do `package.json`:
```json "scripts": { "start": "webpack serve" } ```Teraz uruchom obie aplikacje:
```bash cd remote npm start cd ../host npm start ```Otwórz przeglądarkę i przejdź do `http://localhost:3000`. Powinieneś zobaczyć aplikację Hosta z wyrenderowanym wewnątrz niej komponentem zdalnym.
Wyjaśnienie kluczowych opcji konfiguracyjnych:
- `name`: Unikalna nazwa dla aplikacji.
- `filename`: Nazwa pliku, który będzie zawierał metadane o wyeksponowanych modułach (np. `remoteEntry.js`).
- `exposes`: Mapa nazw modułów do ścieżek plików, określająca, które moduły powinny być eksponowane.
- `remotes`: Mapa nazw aplikacji zdalnych do adresów URL, określająca, gdzie znaleźć plik remoteEntry.js dla każdej aplikacji zdalnej.
- `shared`: Lista modułów, które powinny być współdzielone między aplikacjami hosta i zdalnymi. Opcja `singleton: true` zapewnia, że ładowana jest tylko jedna instancja każdego współdzielonego modułu. Opcja `eager: true` zapewnia, że współdzielony moduł jest ładowany od razu (tj. przed innymi modułami).
Zaawansowane techniki Module Federation
Module Federation oferuje wiele zaawansowanych funkcji, które mogą pomóc w budowaniu jeszcze bardziej zaawansowanych architektur mikro-frontendowych.
Dynamiczne aplikacje zdalne (Remotes)
Zamiast sztywno kodować adresy URL aplikacji zdalnych w konfiguracji Webpacka, można je ładować dynamicznie w czasie rzeczywistym. Pozwala to na łatwą aktualizację lokalizacji aplikacji zdalnych bez konieczności ponownego budowania aplikacji hosta.
Na przykład, można przechowywać adresy URL aplikacji zdalnych w pliku konfiguracyjnym lub w bazie danych i ładować je dynamicznie za pomocą JavaScriptu.
```javascript // W pliku webpack.config.js remotes: { remote: `promise new Promise(resolve => { const urlParams = new URLSearchParams(window.location.search); const remoteUrl = urlParams.get('remote'); // Załóżmy, że remoteUrl to coś w stylu 'http://localhost:3001/remoteEntry.js' const script = document.createElement('script'); script.src = remoteUrl; script.onload = () => { // kluczem w module federation jest to, że zdalna aplikacja jest // dostępna pod nazwą zdefiniowaną w jej konfiguracji (remote) resolve(window.remote); }; document.head.appendChild(script); })`, }, ```Teraz możesz załadować aplikację hosta z parametrem zapytania `?remote=http://localhost:3001/remoteEntry.js`
Wersjonowanie współdzielonych modułów
Module Federation może automatycznie zarządzać wersjonowaniem i deduplikacją współdzielonych modułów, aby zapewnić, że ładowana jest tylko jedna kompatybilna wersja każdego modułu. Jest to szczególnie ważne w przypadku dużych i złożonych aplikacji z wieloma zależnościami.
Możesz określić zakres wersji każdego współdzielonego modułu w konfiguracji Webpacka.
```javascript // W pliku webpack.config.js shared: { react: { singleton: true, eager: true, requiredVersion: '^18.0.0' }, 'react-dom': { singleton: true, eager: true, requiredVersion: '^18.0.0' }, }, ```Niestandardowe loadery modułów
Module Federation pozwala na definiowanie niestandardowych loaderów modułów, które mogą być używane do ładowania modułów z różnych źródeł lub w różnych formatach. Może to być przydatne do ładowania modułów z CDN lub z niestandardowego rejestru modułów.
Dzielenie stanu między mikro-frontendami
Jednym z wyzwań w architekturach mikro-frontendowych jest dzielenie stanu między różnymi mikro-frontendami. Istnieje kilka podejść, które można zastosować, aby sprostać temu wyzwaniu:
- Zarządzanie stanem oparte na URL: Przechowuj stan w adresie URL i używaj go do komunikacji między mikro-frontendami. Jest to proste i bezpośrednie podejście, ale może stać się uciążliwe przy złożonym stanie.
- Niestandardowe zdarzenia (custom events): Używaj niestandardowych zdarzeń do rozgłaszania zmian stanu między mikro-frontendami. Pozwala to na luźne powiązanie między mikro-frontendami, ale zarządzanie subskrypcjami zdarzeń może być trudne.
- Współdzielona biblioteka do zarządzania stanem: Użyj współdzielonej biblioteki do zarządzania stanem, takiej jak Redux czy MobX, do zarządzania stanem całej aplikacji. Zapewnia to scentralizowany i spójny sposób zarządzania stanem, ale może wprowadzić zależność od konkretnej biblioteki.
- Broker wiadomości: Użyj brokera wiadomości, takiego jak RabbitMQ czy Kafka, aby ułatwić komunikację i dzielenie stanu między mikro-frontendami. Jest to bardziej złożone rozwiązanie, ale oferuje wysoki stopień elastyczności i skalowalności.
Dobre praktyki wdrażania mikro-frontendów z Module Federation
Oto kilka dobrych praktyk, o których warto pamiętać podczas wdrażania mikro-frontendów z Module Federation:
- Definiuj jasne granice dla każdego mikro-frontendu: Każdy mikro-frontend powinien być odpowiedzialny za określoną domenę biznesową lub funkcję i mieć dobrze zdefiniowane interfejsy.
- Używaj spójnego stosu technologicznego: Chociaż Module Federation pozwala na używanie różnych technologii dla różnych mikro-frontendów, generalnie dobrym pomysłem jest używanie spójnego stosu technologicznego, aby zmniejszyć złożoność i poprawić łatwość utrzymania.
- Ustal jasne protokoły komunikacji: Zdefiniuj jasne protokoły komunikacji określające, jak mikro-frontendy powinny ze sobą współdziałać.
- Automatyzuj proces wdrażania: Zautomatyzuj proces wdrażania, aby zapewnić, że mikro-frontendy mogą być wdrażane niezależnie i niezawodnie. Rozważ użycie potoków CI/CD i narzędzi typu infrastructure-as-code.
- Monitoruj wydajność swoich mikro-frontendów: Monitoruj wydajność swoich mikro-frontendów, aby identyfikować i rozwiązywać wszelkie wąskie gardła wydajnościowe. Używaj narzędzi takich jak Google Analytics, New Relic czy Datadog.
- Implementuj solidną obsługę błędów: Wdróż solidną obsługę błędów, aby zapewnić odporność aplikacji na awarie.
- Przyjmij zdecentralizowany model zarządzania: Daj zespołom możliwość podejmowania decyzji dotyczących ich własnych mikro-frontendów, zachowując jednocześnie ogólną spójność i jakość.
Przykłady użycia Module Federation w rzeczywistych projektach
Chociaż konkretne studia przypadków są często poufne, oto kilka ogólnych scenariuszy, w których Module Federation może być niezwykle przydatne:
- Platformy e-commerce: Jak wspomniano wcześniej, duże platformy e-commerce mogą używać Module Federation do budowania niezależnych mikro-frontendów dla katalogu produktów, koszyka, procesu płatności i zarządzania kontem użytkownika. Pozwala to różnym zespołom pracować nad tymi funkcjami niezależnie i wdrażać je bez wpływu na inne części aplikacji. Globalna platforma mogłaby dostosowywać funkcje dla różnych regionów za pomocą zdalnych modułów.
- Aplikacje usług finansowych: Aplikacje usług finansowych często mają złożone interfejsy użytkownika z wieloma różnymi funkcjami. Module Federation może być używane do budowania niezależnych mikro-frontendów dla różnych typów kont, platform handlowych i pulpitów raportowych. Funkcje zgodności specyficzne dla niektórych krajów mogą być dostarczane za pomocą Module Federation.
- Portale opieki zdrowotnej: Portale opieki zdrowotnej mogą używać Module Federation do budowania niezależnych mikro-frontendów do zarządzania pacjentami, planowania wizyt i dostępu do dokumentacji medycznej. Różne moduły dla różnych ubezpieczycieli lub regionów mogą być ładowane dynamicznie.
- Systemy zarządzania treścią (CMS): CMS może używać Module Federation, aby umożliwić użytkownikom dodawanie niestandardowych funkcjonalności do swoich stron internetowych poprzez ładowanie zdalnych modułów od deweloperów zewnętrznych. Różne motywy, wtyczki i widżety mogą być dystrybuowane jako niezależne mikro-frontendy.
- Systemy zarządzania nauczaniem (LMS): LMS może oferować kursy rozwijane niezależnie i integrowane w jednolitą platformę za pomocą Module Federation. Aktualizacje poszczególnych kursów nie wymagają ponownego wdrażania całej platformy.
Podsumowanie
JavaScript Module Federation w Webpack 5 zapewnia potężny i elastyczny sposób budowania architektur mikro-frontendowych. Pozwala na współdzielenie kodu między oddzielnie kompilowanymi aplikacjami JavaScript w czasie rzeczywistym, umożliwiając niezależne wdrożenia, różnorodność technologiczną i zwiększoną autonomię zespołów. Postępując zgodnie z najlepszymi praktykami opisanymi w tym przewodniku, możesz wykorzystać Module Federation do budowania skalowalnych, łatwych w utrzymaniu i innowacyjnych aplikacji internetowych.
Przyszłość rozwoju frontendu niewątpliwie zmierza w kierunku architektur modułowych i rozproszonych. Module Federation dostarcza kluczowego narzędzia do budowania tych nowoczesnych systemów, umożliwiając zespołom tworzenie złożonych aplikacji z większą szybkością, elastycznością i odpornością. W miarę dojrzewania technologii możemy spodziewać się pojawienia jeszcze bardziej innowacyjnych zastosowań i najlepszych praktyk.