Solución de Problemas Comunes en Microfrontends con Qiankun

Este artículo aborda desafíos comunes al implementar microfrontends utilizando el framework Qiankun, centrándose en la coexistencia de múltiples sub-aplicaciones y la gestión de CSS, así como en la necesidad de mantener sub-aplicaciones activas y optimizar dependencias compartidas.

Coexistencia de Múltiples Sub-aplicaciones y Conflictos de CSS

Cuando se requiere que varias sub-aplicaciones coexistan simultáneamente, la gestión puede volverse compleja. Si bien Qiankun ofrece mecanismos para registrar múltiples aplicaciones:


import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
 { name: 'appReact', entry: '//localhost:7100', container: '#subapp-container', activeRule: () => window.location.hash.includes('react') },
 { name: 'appVue', entry: '//localhost:7101', container: '#subapp-container', activeRule: () => window.location.hash.includes('vue') },
]);

start({
 singular: false, // Permite que múltiples aplicaciones se ejecuten simultáneamente
});
 

El uso de loadMicroApp proporciona mayor flexibilidad para la carga dinámica y gestión individual:


import React, { useRef, useEffect } from 'react';
import { loadMicroApp } from 'qiankun';

function MicroAppLoader({ appName, appEntry }) {
 const containerRef = useRef(null);
 let microAppInstance = null;

 useEffect(() => {
   microAppInstance = loadMicroApp({
     name: appName,
     entry: appEntry,
     container: containerRef.current,
     props: { message: 'Hola desde el padre' },
   });

   return () => {
     microAppInstance.unmount();
   };
 }, [appName, appEntry]);

 const updateAppName = (newName) => {
   if (microAppInstance) {
     microAppInstance.update({ name: newName });
   }
 };

 return <div ref={containerRef} />;
}
 

Solución 1: Configuración Específica de Componentes (Ej: Ant Design)

Algunas bibliotecas de componentes, como Ant Design, ofrecen configuraciones para especificar el nodo contenedor de sus elementos emergentes (modales, tooltips, etc.). Esto puede ser útil si todas las sub-aplicaciones utilizan la misma biblioteca y se puede configurar de manera cantralizada:


import { ConfigProvider } from 'antd';
import App from './App';

function RootApp() {
 const getContainerForPopups = () => {
   // Lógica para determinar el contenedor correcto basado en el entorno del microfrontend
   const microFrontendContainer = document.getElementById('microfrontend-root');
   return microFrontendContainer || document.body;
 };

 return (
   <ConfigProvider getPopupContainer={getContainerForPopups}>
     <App />
   </ConfigProvider>
 );
}

export default RootApp;
 

Sin embargo, este enfoque es limmitado y no funciona bien con componentes de terceros que no exponen una opción similar.

Solución 2: Intercepción de Métodos del DOM

Una solución más generalizada implica interceptar métodos del DOM, como appendChild, para redirigir la inserción de nodos a un contenedor específico dentro de la sub-aplicación activa. Se puede lograr mediante el uso de Proxies o mediante la reasignación directa de métodos.

Usando Proxy:


// Guardar la referencia original del método appendChild
const originalBodyAppendChild = document.body.appendChild;
let subAppContainer = null; // Referencia al contenedor raíz de la sub-aplicación

function setSubAppContainer(containerElement) {
 subAppContainer = containerElement;
}

const proxiedBodyAppendChild = new Proxy(originalBodyAppendChild, {
 apply(target, thisArg, argumentsList) {
   const elementToAppend = argumentsList[0];
   if (subAppContainer) {
     console.log('Redirigiendo appendChild al contenedor de la sub-aplicación.');
     subAppContainer.appendChild(elementToAppend);
   } else {
     // Si no hay contenedor de sub-aplicación, usar el comportamiento original
     console.warn('No se encontró contenedor de sub-aplicación, usando document.body.');
     target.apply(thisArg, argumentsList);
   }
   return elementToAppend; // Devolver el elemento añadido
 }
});

function enableSubAppDomInterception() {
 document.body.appendChild = proxiedBodyAppendChild;
}

function disableSubAppDomInterception() {
 if (document.body.appendChild === proxiedBodyAppendChild) {
   document.body.appendChild = originalBodyAppendChild;
 }
}
 

Nota Importante: Al interceptar appendChild, también es crucial considerar y gestionar removeChild de manera similar para evitar inconsistencias.

Alternativa sin Proxy (Compatibilidad):


const originalAppend = document.body.appendChild;
let targetDom = document.body; // Por defecto, el body

const customAppendChild = (newNode) => {
 try {
   targetDom.appendChild(newNode);
 } catch (error) {
   console.error("Error al añadir nodo:", error);
   // Lógica de fallback o manejo de errores
 }
};

document.body.appendChild = customAppendChild;

// Para cambiar el contenedor dinámicamente:
// targetDom = document.getElementById('mi-contenedor-interno');

// Para restaurar:
// document.body.appendChild = originalAppend;
 

Consideración Adicional: Con React 17+, la delegación de eventos se realiza en el nodo raíz del renderizado de React (ej: un div#app). Asegurarse de que los nodos añadidos no se inserten en un ancestro que interfiera con la delegación de eventos de React es vital para que los componentes interactivos funcionen correctamente.

Mantenimiento de Estado (Keep-Alive) de Sub-aplicaciones

Aunque el modelo de microfrontends ganeralmente promueve la independencia y el ciclo de vida gestionado por la carga/descarga, existen escenarios donde se requiere mantener el estado de una sub-aplicación incluso cuando no está visible. Se puede emplear bibliotecas como react-activation para lograr este efecto, montando el DOM de la sub-aplicación en un elemento que no sea directamente gestionado por el enrutamiento principal.

Es importante tener en cuenta que, incluso si una página no se muestra visualmente, la presencia de react-router puede mantener mapeos de rutas activos. Puede ser necesario desactivar ciertas lógicas de enrutamiento o validaciones (como la detección de 404) que podrían interferir con el comportamiento esperado de las sub-aplicaciones "inactivas".

Extracción de Dependencias Comunes

Qiankun, al igual que otros enfoques de microfrontends, no recomienda fuertemente compartir dependencias en tiempo de ejecución entre sub-aplicaciones debido a la complejidad y posibles conflictos de versiones. La estrategia principal sugerida es utilizar la opción external en la configuración de la herramienta de build (como Webpack) para que la aplicación principal cargue las dependencias comunes necesarias. Esto asegura que cada dependencia se cargue solo una vez.

La investigación continua en el ecosistema de microfrontends podría revelar soluciones más robustas y dinámicas para la gestión de dependencias compartidas en el futuro.

Etiquetas: qiankun microfrontends css-isolation React Vue

Publicado el 6-9 03:33