Configurar Webpack 5 con Module Federation

Lectura
20 min~5 min lectura

Concepto clave

Module Federation es una funcionalidad de Webpack 5 que permite cargar código de aplicaciones independientes en tiempo de ejecución, creando un ecosistema de micro-frontends. Imagina un centro comercial donde cada tienda (micro-frontend) opera de forma autónoma, pero comparte servicios comunes como estacionamiento (dependencias) y permite a los clientes moverse entre ellas sin reiniciar su experiencia. En términos técnicos, Module Federation habilita la composición dinámica de bundles entre diferentes builds de Webpack, eliminando la necesidad de monorepos o builds monolíticos.

La arquitectura se basa en dos roles principales: host (aplicación contenedora que consume módulos remotos) y remote (aplicación que expone módulos). La magia ocurre mediante un manifesto de federación que Webpack genera automáticamente, describiendo qué módulos están disponibles y cómo acceder a ellos. Esto permite que equipos distribuidos desarrollen y desplieguen independientemente, mientras mantienen una experiencia de usuario cohesiva.

Cómo funciona en la práctica

Configurar Module Federation implica tres pasos principales en tu archivo webpack.config.js:

  1. Definir la configuración de federación usando el plugin ModuleFederationPlugin
  2. Especificar qué módulos expones (remotes) o consumes (host)
  3. Configurar las dependencias compartidas para evitar duplicación de librerías

Veamos un ejemplo básico: supongamos que tienes una aplicación de dashboard (host) que necesita incorporar un widget de gráficos desde una aplicación separada (remote). Primero, en la aplicación remote, configuras Webpack para exponer el componente ChartWidget. Luego, en el host, configuras la referencia a ese remote y lo importas dinámicamente en tu código. Webpack se encarga de resolver las dependencias y cargar el código cuando sea necesario, sin requerir un deploy conjunto.

Código en acción

Configuración de una aplicación remote que expone un componente:

// webpack.config.js de la aplicación remote (widget-app)
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ... otras configuraciones de Webpack
  plugins: [
    new ModuleFederationPlugin({
      name: 'widgetApp',
      filename: 'remoteEntry.js',
      exposes: {
        './ChartWidget': './src/components/ChartWidget.jsx',
        './DashboardHeader': './src/components/DashboardHeader.jsx'
      },
      shared: {
        react: { singleton: true, eager: false },
        'react-dom': { singleton: true, eager: false }
      }
    })
  ]
};

Configuración del host que consume el módulo remoto:

// webpack.config.js de la aplicación host (main-dashboard)
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ... otras configuraciones
  plugins: [
    new ModuleFederationPlugin({
      name: 'mainDashboard',
      remotes: {
        widgetApp: 'widgetApp@http://localhost:3001/remoteEntry.js'
      },
      shared: {
        react: { singleton: true, eager: false },
        'react-dom': { singleton: true, eager: false },
        'chart.js': { singleton: true }
      }
    })
  ]
};

Uso en el código del host con importación dinámica:

// En main-dashboard/src/App.jsx
import React, { Suspense } from 'react';

const RemoteChartWidget = React.lazy(() => import('widgetApp/ChartWidget'));

function App() {
  return (
    

Dashboard Principal

Cargando widget...
}> ); }

Errores comunes

  • Versiones incompatibles de dependencias compartidas: Si host y remote usan diferentes versiones de React sin configurar correctamente el shared, pueden ocurrir errores de runtime. Solución: Usa singleton: true y especifica versiones requeridas en shared.
  • Configuración incorrecta de CORS: Al desarrollar localmente, los remotes en diferentes puertos pueden bloquearse por políticas de same-origin. Solución: Configura headers adecuados en el servidor de desarrollo o usa un proxy.
  • Falta de manejo de carga asíncrona: No envolver los componentes remotos en Suspense puede causar errores cuando el módulo no se ha cargado. Solución: Siempre usa React.lazy con Suspense y proporciona un fallback.
  • Nombres de módulos mal referenciados: Usar './ChartWidget' en el host cuando el remote expone './ChartWidget' con diferente casing. Solución: Verifica que los paths en exposes y imports coincidan exactamente.
  • Omitir eager: false en shared: Esto puede causar que las dependencias se carguen duplicadas. Solución: Configura eager: false para cargar bajo demanda, mejorando el performance inicial.

Checklist de dominio

  1. He configurado correctamente ModuleFederationPlugin en al menos una aplicación host y una remote
  2. Puedo exponer y consumir múltiples módulos entre aplicaciones independientes
  3. He implementado shared dependencies para librerías comunes como React y ReactDOM
  4. Sé cómo depurar problemas de carga de módulos usando las herramientas de desarrollador del navegador
  5. Puedo explicar la diferencia entre singleton y eager en la configuración de shared
  6. He configurado entornos de desarrollo con múltiples remotes en diferentes puertos
  7. Entiendo cómo Module Federation afecta el bundle final y las estrategias de caching

Crear un ecosistema de micro-frontends con Module Federation

En este ejercicio, configurarás dos aplicaciones React independientes que se comunican mediante Module Federation. Sigue estos pasos:

  1. Crea dos nuevas aplicaciones React usando Create React App (CRA) o tu setup preferido. Nómbralas app-host y app-remote.
  2. En app-remote, ejecuta npm install webpack@5 webpack-cli webpack-dev-server para asegurar Webpack 5. Luego, configura webpack.config.js para exponer un componente llamado WelcomeMessage que reciba una prop user y muestre un saludo personalizado.
  3. En app-host, configura webpack.config.js para consumir el módulo remoto de app-remote. Asegúrate de compartir React y ReactDOM como dependencias singleton.
  4. En app-host, crea un componente principal que importe dinámicamente WelcomeMessage desde app-remote y lo renderice pasando user="Arquitecto" como prop.
  5. Inicia ambos servidores de desarrollo en puertos diferentes (ej: 3000 y 3001) y verifica que el componente remoto se renderice correctamente en el host.
  6. Opcional: Modifica el componente WelcomeMessage en app-remote y observa cómo los cambios se reflejan en el host sin necesidad de reiniciar o redeployar.
Pistas
  • Recuerda que CRA puede requerir eject o usar craco para personalizar Webpack 5
  • Asegúrate de que remoteEntry.js sea accesible desde el host verificando la URL en remotes
  • Usa React.lazy y Suspense para manejar la carga asíncrona del módulo remoto

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.