@ssi-lib/mf-loader
v1.0.3
Published
Carga Micro-Frontends en hosts React con [Module Federation](https://module-federation.io/).
Downloads
125
Readme
@ssi-lib/mf-loader
Carga Micro-Frontends en hosts React con Module Federation.
No renderiza UI de loading ni error. Por defecto deja marcadores DOM (#mfe-loading, #mfe-fallback) para que el host los gestione.
Instalación
npm install @ssi-lib/mf-loaderPeer dependencies: react, react-dom, @module-federation/bridge-react, @module-federation/enhanced (>=18 / >=2.0.0).
1. Básico — mínimo para funcionar
Environment
// src/environments/environment.ts
export const environment = {
mfeUrls: {
commonsManagementMfe: 'http://localhost:7101',
},
};Bootstrap (una vez, antes de <App />)
// src/bootstrap.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { initModuleFederation } from '@ssi-lib/mf-loader';
import { environment } from './environments/environment';
import App from './App';
initModuleFederation(environment);
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);Montar un remoto
import { LoadComponent } from '@ssi-lib/mf-loader';
<LoadComponent
lazyElement={{
mfe: 'commonsManagementMfe',
component: 'PartyIndividualForm',
}}
/>Sin url en lazyElement → busca en environment.mfeUrls[mfe].
2. Environment completo — con precarga
prefetchMfes precarga el remoteEntry.js de esos MFE al arranque (en idle). No descarga el chunk del componente expuesto; solo acelera la primera carga.
// src/environments/environment.ts
export const environment = {
/** name → base URL (la librería añade /remoteEntry.js) */
mfeUrls: {
commonsManagementMfe: 'http://localhost:7101',
// customerManagementMfe: 'http://localhost:7102',
},
/** nombres de mfeUrls cuyo remoteEntry se precarga al arranque */
prefetchMfes: ['commonsManagementMfe'],
};initModuleFederation(environment);| Paso | Descarga remoteEntry.js | Descarga chunk del expose |
|------|---------------------------|---------------------------|
| initModuleFederation + prefetchMfes | Sí (en idle) | No |
| LoadComponent al montar | Si no estaba prefetcheado | Sí |
3. Intermedio — props, rutas y marcadores
Props al remoto
<LoadComponent
lazyElement={{ mfe: 'commonsManagementMfe', component: 'PartyIndividualForm' }}
initSettings={{ partyId: '123', mfSaveFn: true }}
onResultData={(data) => console.log(data)}
/>React Router
<Route
path="/party"
element={
<LoadComponent
lazyElement={{ mfe: 'customerManagementMfe', component: 'PartyIndividualForm' }}
/>
}
/>Loading y error — marcadores por defecto
Sin loading ni fallback, la librería inserta:
| Marcador | Cuándo |
|----------|--------|
| #mfe-loading | Mientras carga |
| #mfe-fallback | Si falla la carga |
El host los detecta y muestra su UI. Con ssi-loading-lib:
import { initSsiLoadingLib } from 'ssi-loading-lib';
initSsiLoadingLib();
initModuleFederation(environment);Loading y error — UI propia (opcional)
<LoadComponent
lazyElement={{ mfe: 'commonsManagementMfe', component: 'PartyIndividualForm' }}
loading={<Spinner />}
fallback={({ error }) => <div role="alert">{error.message}</div>}
/>4. Avanzado
URL explícita (no está en el environment)
<LoadComponent
lazyElement={{
mfe: 'externalMfe',
component: 'SomeForm',
url: 'https://cdn.example.com/mfe',
}}
/>MFE dinámico (URL desde API)
const [remote, setRemote] = useState<{ mfe: string; component: string; url: string } | null>(null);
useEffect(() => {
fetch(`/api/remotes/${id}`).then((r) => r.json()).then(setRemote);
}, [id]);
if (!remote) return null;
return (
<LoadComponent
lazyElement={{ mfe: remote.mfe, component: remote.component, url: remote.url }}
/>
);Sin URL resuelta → null
Si no hay lazyElement.url y el mfe no está en mfeUrls (o no se llamó initModuleFederation):
// ❌ renderiza null
<LoadComponent lazyElement={{ mfe: 'unknownMfe', component: 'SomeForm' }} />Forzar remontaje (nueva versión del MFE)
<LoadComponent
key={`${mfe}-${version}`}
lazyElement={{ mfe, component, url: `${baseUrl}?v=${version}` }}
/>Configuración global (MfLoaderConfig)
Segundo argumento opcional de initModuleFederation. Si no llega un campo, usa el default:
| Opción | Default |
|--------|---------|
| remoteEntryFile | remoteEntry.js |
| loadingMarkerId | mfe-loading |
| fallbackMarkerId | mfe-fallback |
initModuleFederation(environment, { loadingMarkerId: 'app-loading' });getMfLoaderConfig() devuelve la config activa.
createRemoteComponent (FlowStepper, factories)
Sin wrapper LoadComponent. Requiere tu propio <Suspense>.
const RemoteForm = useMemo(
() =>
createRemoteComponent(
{
mfe: 'commonsManagementMfe',
component: 'OrganizationForm',
url: environment.mfeUrls.commonsManagementMfe,
},
undefined,
{ loading: <div id="mfe-loading" /> },
),
[],
);
return (
<Suspense fallback={<div id="mfe-loading" />}>
<RemoteForm initSettings={{ partyId, mfSaveFn: true }} onResultData={onResult} />
</Suspense>
);Expose distinto al de component
const RemoteHeader = createRemoteComponent(
{ mfe: 'commonsManagementMfe', component: 'DefaultExpose', url: environment.mfeUrls.commonsManagementMfe },
'AppHeader', // carga commonsManagementMfe/AppHeader
);Ejemplo completo — host de punta a punta
// environment.ts
export const environment = {
mfeUrls: {
commonsManagementMfe: 'http://localhost:7101',
customerManagementMfe: 'http://localhost:7102',
},
prefetchMfes: ['commonsManagementMfe'],
};// bootstrap.tsx
import { initSsiLoadingLib } from 'ssi-loading-lib';
import { initModuleFederation } from '@ssi-lib/mf-loader';
import { environment } from './environments/environment';
initSsiLoadingLib();
initModuleFederation(environment);// PartyPage.tsx
import { LoadComponent } from '@ssi-lib/mf-loader';
export const PartyPage = () => (
<LoadComponent
lazyElement={{ mfe: 'commonsManagementMfe', component: 'PartyIndividualForm' }}
initSettings={{ partyId: '123', mfSaveFn: true }}
onResultData={(data) => console.log(data)}
/>
);Referencia
Resolución de URL
lazyElement.url → usa esa URL
environment.mfeUrls[mfe] → si no hay url en lazyElement
null → LoadComponent no renderiza nadaLa URL base se normaliza a .../remoteEntry.js.
Props de LoadComponent
| Prop | Descripción |
|------|-------------|
| lazyElement | { mfe, component, url? } |
| loading | Default: <div id="mfe-loading" /> |
| fallback | Default: <div id="mfe-fallback" /> |
| ...props | Al remoto (initSettings, onResultData, etc.) |
API
| Export | Uso |
|--------|-----|
| initModuleFederation(env, options?) | Bootstrap — registra remotos y precarga |
| LoadComponent | Cargar un MFE (habitual) |
| createRemoteComponent | Bridge sin wrapper (avanzado) |
| getMfLoaderConfig() | Config activa |
| MFEConfig | { mfe, component, url? } |
| HostEnvironment | { mfeUrls, prefetchMfes? } |
| MfLoaderConfig | Opciones globales del loader |
Convenciones
| Host | Remoto |
|------|--------|
| lazyElement.mfe | name en module-federation.config.ts |
| lazyElement.component | clave en exposes sin ./ |
| mfeUrls[mfe] | URL base del servidor del remoto |
Caché
- No cachea componentes entre refrescos (F5 limpia el estado).
remoteEntry.js→Cache-Control: no-cacheal redesplegar.- Chunks con hash → cache inmutable.
- Forzar versión nueva:
?v=1.4.2en URL okeyen React.
