Перейти к содержимому

12. Module Federation

Module Federation — революционная возможность Webpack 5, которая позволяет нескольким независимым приложениям (микрофронтендам) динамически загружать код друг от друга во время выполнения. Это фундаментально меняет архитектуру крупных frontend-приложений.

Традиционный монолит vs Микрофронтенды:

Монолит: Микрофронтенды с MF:
┌─────────────────────────┐ ┌─────────┐ ┌──────────┐ ┌──────────┐
│ Огромный монорепо │ │ Shell │ │ Header │ │ Checkout │
│ ┌─────┐┌──────┐┌──────┐│ │ App │ │ MFE │ │ MFE │
│ │Team1││Team2 ││Team3 ││ │ │ │ │ │ │
│ └─────┘└──────┘└──────┘│ │ host │ │ remote │ │ remote │
└─────────────────────────┘ └────┬────┘ └────┬─────┘ └────┬─────┘
Один деплой для всех └─────────────┴─────────────┘
Независимые деплои!
  • Host (Shell) — главное приложение, загружает ремоуты
  • Remote — приложение, экспортирующее компоненты/модули
  • Shared — общие зависимости (React, Redux) — загружаются один раз
// webpack.config.js в remote приложении (header-app)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
output: {
publicPath: 'http://localhost:3001/', // Важно! Абсолютный URL
},
plugins: [
new ModuleFederationPlugin({
name: 'headerApp', // Уникальное имя remote
filename: 'remoteEntry.js', // Точка входа для host
// Что экспортируем
exposes: {
'./Header': './src/components/Header',
'./Navigation': './src/components/Navigation',
'./UserMenu': './src/components/UserMenu',
},
// Общие зависимости
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
};
// webpack.config.js в host приложении (shell)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
// Откуда загружать ремоуты
remotes: {
headerApp: 'headerApp@http://localhost:3001/remoteEntry.js',
checkoutApp: 'checkoutApp@http://localhost:3002/remoteEntry.js',
productApp: 'productApp@http://localhost:3003/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
};
import { lazy, Suspense } from 'react';
// Динамический импорт из remote
const Header = lazy(() => import('headerApp/Header'));
const Checkout = lazy(() => import('checkoutApp/Checkout'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading header...</div>}>
<Header />
</Suspense>
<main>
<Suspense fallback={<div>Loading checkout...</div>}>
<Checkout />
</Suspense>
</main>
</div>
);
}
Окно терминала
npm install --save-dev @module-federation/typescript
// В конфиге webpack remote:
plugins: [
new FederatedTypesPlugin({
disableTypeCompilation: false,
}),
]

Для runtime конфигурации (URL из API):

// Динамическая загрузка remote
async function loadRemoteModule(remoteUrl, scope, module) {
await __webpack_init_sharing__('default');
const container = window[scope];
await container.init(__webpack_share_scopes__.default);
const factory = await window[scope].get(module);
return factory();
}
// Использование:
const { Header } = await loadRemoteModule(
'http://dynamic-host.com/remoteEntry.js',
'dynamicApp',
'./Header'
);

Ниже — интерактивная визуализация архитектуры Module Federation с host и remote приложениями.