Dans le paysage en évolution du développement frontend, les applications monolithiques deviennent souvent des goulets d'étranglement pour les grandes équipes d'ingénierie. À mesure que les applications grossissent, la complexité de la gestion des dépendances, des cycles de déploiement et de la propriété du code augmente également. C'est ici que l'architecture Micro-Frontend brille, permettant aux équipes de construire, tester et déployer des applications frontend indépendantes. Alors que les premières implémentations s'appuyaient sur des shims iframe complexes ou des solutions de regroupement au moment de l'exécution, Webpack 5 a introduit une solution native et robuste : Module Federation.
Cet article explore comment tirer parti du Module Federation Plugin de Webpack 5 pour créer des micro-frontends véritablement évolutifs et indépendants, qui peuvent être partagés et consommés entre différentes applications sans la surcharge traditionnelle.
Qu'est-ce que Module Federation ?
Module Federation est un concept qui permet aux applications JavaScript d'importer dynamiquement du code depuis d'autres environnements d'exécution (navigateurs, serveurs ou Web Workers) au moment de l'exécution. Contrairement aux bundlers traditionnels qui résolvent les dépendances au moment de la construction, Module Federation les résout au moment de l'exécution. Cela signifie que l'Application A peut exécuter du code écrit dans l'Application B, comme s'il était local, à condition que les deux applications soient en cours d'exécution.
Les principaux avantages sont les suivants :
- Déploiements indépendants : Les équipes peuvent mettre à jour et déployer leur micro-frontend spécifique sans toucher à l'application hôte.
- Dépendances partagées : Des bibliothèques critiques comme React ou Redux peuvent être partagées entre les applications pour réduire la taille du bundle et assurer la cohérence.
- Agnosticisme technologique : Bien que Webpack soit l'outil principal, le protocole permet l'intégration avec d'autres systèmes de construction, bien que Webpack offre l'implémentation la plus mature.
Configuration des applications Hôte et Distante
Pour illustrer Module Federation, supposons un scénario avec deux applications :
- App Hôte : L'application shell principale qui orchestre l'interface utilisateur.
- App Distante : Un module spécifique à une fonctionnalité (par exemple, un tableau de bord utilisateur ou un composant de chat) qui sera consommé par l'Hôte.
Étape 1 : Exposition des modules dans l'App Distante
D'abord, nous devons configurer l'App Distante pour exposer ses composants. Dans le fichier webpack.config.js de l'application distante, nous importons ModuleFederationPlugin et définissons le champ exposes.
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... autres configurations
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./UserProfile': './src/components/UserProfile',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
}),
new HtmlWebpackPlugin({
template: './public/index.html'
})
]
};
Ici, nous exposons ./UserProfile. Nous configurons également les dépendances shared. En définissant singleton: true, nous nous assurons qu'une seule instance de React est chargée entre les deux applications, évitant ainsi les incohérences d'état et réduisant la taille du payload.
Étape 2 : Consommation des modules dans l'App Hôte
Ensuite, nous configurons l'App Hôte pour consommer le module exposé. Nous ajoutons la configuration remote dans le ModuleFederationPlugin de l'application hôte.
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
})
Le champ remotes indique à Webpack où trouver le fichier d'entrée distant. Vous pouvez utiliser une URL statique ou un point de terminaison dynamique qui renvoie la configuration en fonction de l'environnement. Notez que la configuration shared doit correspondre à celle de l'application distante pour garantir la compatibilité.
Importation dynamique dans React
Avec la configuration en place, vous pouvez importer dynamiquement le composant distant en utilisant l'API System.import (polyfillisée par Webpack) ou les importations dynamiques standard. Voici un exemple dans un composant React :
import React, { lazy, Suspense } from 'react';
// Chargement paresseux du composant distant
const UserProfile = lazy(() => import('remoteApp/UserProfile'));
const HostComponent = () => {
return (
Chargement... }>