Micro-frontends con Angular y Webpack Module Federation
Descripción general
Module Federation ha tenido un impacto notable en el panorama de los micro-frontends. Integrado con Angular, ofrece una solución robusta y escalable para arquitecturas front-end distribuidas. Esta guía analiza un ejemplo práctico y detalla la configuración del ModuleFederationPlugin de Webpack en una aplicación shell de Angular y en una aplicación remota.
Requisitos previos
Antes de empezar con la configuración, asegúrate de cumplir los siguientes requisitos previos:
- Node.js y npm: asegúrate de tener instalados Node.js y npm.
- Conocimientos de Angular y Webpack: es necesario un conocimiento básico de Angular y Webpack.
- Module Federation: se da por hecho que ya conoces Module Federation.
Pasos preparatorios
Forzar la resolución de Webpack 5
Los proyectos de Angular CLI suelen venir preconfigurados con Webpack, pero para asegurarte de que Module Federation sea totalmente compatible, necesitas optar explícitamente por Webpack 5.
Abre tu package.json y añade una clave resolutions para forzar el uso de Webpack 5:
Especificar el gestor de paquetes en Angular CLI
Añadir una configuración de Webpack personalizable
Tienes un par de opciones para exponer la configuración de Webpack, como usar Ngx-build-plus o @angular-builders/custom-webpack.
En este ejemplo usaremos la segunda.
Primero, instala el paquete:
Después, actualiza tu archivo angular.json para usar este builder personalizado en los comandos build y serve:
La configuración personalizada de Webpack se combinará con la configuración por defecto de Angular, así que solo tienes que especificar los cambios necesarios para Module Federation.
Configuración de Webpack para la aplicación shell
Configuración de un nombre de salida único
Por defecto, Webpack usa el nombre del package.json. Sin embargo, para evitar conflictos, sobre todo en monorepos, se recomienda definir manualmente un nombre único.
NOTA: si no usas un monorepo y tu package.json ya tiene nombres únicos, puedes saltarte este paso.
Optimización del runtime chunk
Debido a un bug actual, es imprescindible establecer la optimización runtimeChunk en false; de lo contrario, la configuración de Module Federation dejará de funcionar.
Añadir el plugin de Module Federation
En tu webpack.config.ts, añade ModuleFederationPlugin al array de plugins:
Aquí, en el objeto remotes, asociamos los nombres de los módulos remotos con sus respectivas ubicaciones. La clave ('mf1' en este ejemplo) es el nombre que se usa para importar el módulo en la aplicación shell. El valor indica la ubicación del archivo remoto, que en este ejemplo es http://localhost:4300/mf1.js.
Configurar el módulo o la aplicación remota
Definir un nombre de salida único y deshabilitar el runtime chunk
Igual que en la aplicación shell, define un nombre de salida único y deshabilita la optimización runtimeChunk:
Añadir el plugin de Module Federation
Configura ModuleFederationPlugin de la siguiente manera:
Aquí, las propiedades filename y name especifican el nombre del archivo JavaScript y el namespace del contenedor del módulo en el objeto global window. Son exactamente los valores que usa la aplicación shell al cargar el módulo remoto.
Exponer módulos
El objeto exposes especifica los módulos que se exportan. En este ejemplo:
./Contactexporta unNgModulede Angular con rutas hijas../Clockexporta un componente de Angular para renderizarlo en runtime.
Enrutamiento en Angular
Declarar los módulos remotos
Antes de poder usar los módulos remotos, tienes que informar a TypeScript de su existencia, ya que se cargarán dinámicamente en runtime.
Crea un nuevo archivo de definiciones de TypeScript, remote-modules.d.ts, junto a tu módulo de enrutamiento:
Lazy loading de módulos remotos en las rutas
Igual que harías con los módulos nativos con lazy loading, ahora puedes importar módulos remotos en la configuración de rutas de Angular.
Modifica la configuración de rutas de la siguiente manera:
Creación dinámica de componentes a partir de módulos remotos
Crear componentes dinámicamente a partir de módulos remotos ofrece un nivel de integración más avanzado. Para ello hay que configurar un servicio y una directiva que se encarguen del renderizado dinámico.
El servicio Remote Module Loader
Este servicio se encarga de cargar dinámicamente los módulos remotos y de resolver las factorías de componentes.
La directiva Remote Component Renderer
Esta directiva estructural crea componentes dinámicamente dentro de su propio view container usando la factoría de componentes obtenida del servicio Remote Module Loader.
Uso en la vista
En tu vista de Angular, puedes usar la directiva así:
Dependencias shared
Importancia de las dependencias shared
La sección shared de la configuración de Webpack desempeña un papel clave a la hora de definir los módulos comunes entre la shell y el módulo remoto. Hacerlo puede reducir considerablemente el tamaño del bundle y mejorar la experiencia de usuario.
Gestión de discrepancias de versiones
Webpack puede lanzar errores de runtime debido a discrepancias de versión mayor entre la shell y las aplicaciones remotas. Mantener las versiones sincronizadas es importante para los singletons shared
Versionado semántico y flexibilidad
Webpack se rige por el versionado semántico al resolver las dependencias shared. Es recomendable permitir cierta flexibilidad en la selección de versiones usando operadores como ^ o >=. Así se garantiza que solo se carguen las versiones necesarias y se minimiza el riesgo de cargar varias versiones de una biblioteca en conflicto.
Resumen
Esta guía te ha mostrado la integración dinámica de módulos remotos en una aplicación Angular aprovechando Module Federation de Webpack. En concreto, has aprendido:
- Cómo configurar Yarn como gestor de paquetes.
- A personalizar la configuración de Webpack para el build de Angular.
- A usar Module Federation tanto en la aplicación shell como en las aplicaciones micro-frontend.
- A hacer lazy loading de módulos remotos en el enrutamiento de Angular.
- A crear componentes dinámicamente a partir de módulos remotos.
Para una configuración lista para producción hacen falta pasos adicionales, que se cubrirán en una guía futura. No dudes en contactarnos a través de nuestras redes sociales si tienes cualquier pregunta sobre esta técnica.