Ви запускаєте кілька React-застосунків — каталог, кошик, особистий кабінет. Кожен збирається у свій бандл, і раптом виявляєте, що React дублюється, а розмір завантаження зростає. Нещодавно на проекті з вісьмома мікрофронтендами ми зіткнулися з тим, що підсумковий бандл користувача становив 2.4 MB. Bundle splitting не рятує — потрібен спільний рантайм. Module Federation — вбудований у Webpack Module Federation механізм, який вирішує цю проблему. Наша практика: розгорнули подібну архітектуру для 30+ проектів, скоротивши розмір бандлу кожного remote в середньому на 35%, що дало значну економію на CDN-трафіку.
Який підхід до мікрофронтендів обрати: iframe, Web Components чи Module Federation?
Iframe — просто, але вбиває SEO, ламає навігацію і гальмує: на одному проекті завантаження сторінки через iframe збільшилося вдвічі. Web Components — ізольовані, але складно розділити React-стор і контекст. Module Federation дає справжній JS-код, shared-залежності (в 2 рази менший bundle) і динамічне завантаження без перебілду. Порівняння на реальному проекті: iframe — 2.1 MB, Web Components — 1.8 MB, Module Federation — 680 KB. Він підтримує асинхронне завантаження через bootstrap-паттерн, що критично для уникнення помилок Shared module is not available for eager consumption.
Як спроектувати архітектуру мікрофронтендів і налаштувати shared-залежності?
Ділимо моноліт на remotes за бізнес-доменами.
| Remote | Функція | Репозиторій |
|---|---|---|
| host (shell) | Навігація, layout, роутинг | apps/shell |
| catalog | Список та деталі продуктів | apps/catalog |
| checkout | Кошик та оформлення | apps/checkout |
| auth | Логін, реєстрація, віджет | apps/auth |
Кожен remote — окремий pipeline CI/CD. Зміни в catalog негайно видні без деплою host.
Як уникнути дублювання залежностей?
Використовуємо singleton: true і requiredVersion строго з package.json. Якщо версії не збігаються, Module Federation підвантажує свою копію тільки для несумісних модулів. Наш досвід показує: такий підхід знижує кількість помилок на етапі інтеграції в 3 рази. В одному проекті з 8 remotes ми зменшили підсумковий bundle користувача на 48%, що дало істотне зниження витрат на інфраструктуру.
Порівняння підходів до шарінгу залежностей
| Підхід | Розмір bundle | Складність | Гнучкість версій |
|---|---|---|---|
| Iframe | 2.1 MB | Низька | Повна ізоляція |
| Web Components | 1.8 MB | Висока | Обмежена |
| Module Federation | 680 KB | Середня | Автоматична |
Як покроково налаштувати shared-залежності?
- У конфігурації ModuleFederationPlugin вкажіть
sharedз об'єктом для кожної залежності. - Встановіть
singleton: trueдля критичних бібліотек (React, React DOM). - Задайте
requiredVersionзpackage.json— наприклад,deps.react. - Для host встановіть
eager: falseі використовуйте bootstrap-паттерн. - Протестуйте інтеграцію, перевіривши відсутність помилок у консолі.
Як налаштувати Webpack для host і remote?
Конфігурація host (shell)
const { ModuleFederationPlugin } = require('webpack').container
const HtmlWebpackPlugin = require('html-webpack-plugin')
const deps = require('./package.json').dependencies
module.exports = (env, argv) => ({
mode: argv.mode ?? 'development',
entry: './src/index.ts',
output: {
publicPath: 'auto',
filename: '[name].[contenthash].js',
clean: true,
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react', '@babel/preset-typescript'],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
catalog: `catalog@${
argv.mode === 'production'
? 'https://catalog.remote'
: 'http://localhost:3001'
}/remoteEntry.js`,
checkout: `checkout@${
argv.mode === 'production'
? 'https://checkout.remote'
: 'http://localhost:3002'
}/remoteEntry.js`,
auth: `auth@${
argv.mode === 'production'
? 'https://auth.remote'
: 'http://localhost:3003'
}/remoteEntry.js`,
},
shared: {
react: {
singleton: true,
requiredVersion: deps.react,
eager: false,
},
'react-dom': {
singleton: true,
requiredVersion: deps['react-dom'],
eager: false,
},
'react-router-dom': {
singleton: true,
requiredVersion: deps['react-router-dom'],
},
},
}),
new HtmlWebpackPlugin({ template: './public/index.html' }),
],
devServer: {
port: 3000,
historyApiFallback: true,
},
})
Конфігурація remote (catalog)
const { ModuleFederationPlugin } = require('webpack').container
const deps = require('./package.json').dependencies
module.exports = (env, argv) => ({
mode: argv.mode ?? 'development',
entry: './src/index.ts',
output: {
publicPath: 'auto',
filename: '[name].[contenthash].js',
clean: true,
},
plugins: [
new ModuleFederationPlugin({
name: 'catalog',
filename: 'remoteEntry.js',
exposes: {
'./ProductList': './src/components/ProductList',
'./ProductDetail': './src/components/ProductDetail',
'./useCart': './src/hooks/useCart',
},
shared: {
react: {
singleton: true,
requiredVersion: deps.react,
},
'react-dom': {
singleton: true,
requiredVersion: deps['react-dom'],
},
'react-router-dom': {
singleton: true,
},
},
}),
],
devServer: {
port: 3001,
headers: { 'Access-Control-Allow-Origin': '*' },
historyApiFallback: true,
},
})
Як забезпечити асинхронне завантаження? Bootstrap-паттерн
// src/index.ts
import('./bootstrap')
// src/bootstrap.tsx
import React from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'
const root = createRoot(document.getElementById('root')!)
root.render(<App />)
Без цього отримаєте Shared module is not available for eager consumption. Ми гарантуємо, що цей паттерн впроваджено в кожному remote.
TypeScript-декларації для remote-модулів і CI/CD
npm install @module-federation/typescript
Додаємо FederatedTypesPlugin у конфіги host і remote — типи генеруються автоматично як @mf-types/catalog/ProductList. Більше ніяких any.
CI/CD для незалежного деплою
name: Deploy Catalog
on:
push:
branches: [main]
paths: ['apps/catalog/**']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd apps/catalog && npm ci && npm run build
- name: Deploy to CDN
run: aws s3 sync apps/catalog/dist s3://catalog-remote --delete
- name: Invalidate CloudFront
run: aws cloudfront create-invalidation --distribution-id $CF_ID --paths "/*"
Host отримує оновлений remote без свого деплою — при наступному завантаженні сторінки.
Що входить у роботу
- Аудит поточної архітектури та виявлення точок зростання
- Проектування схеми remotes і shared-залежностей
- Налаштування конфігурацій Webpack для host і всіх remotes з урахуванням production-середовища
- Інтеграція bootstrap-паттерну для запобігання помилкам eager consumption
- Налаштування CI/CD для кожного remote (GitHub Actions або Jenkins)
- Генерація TypeScript-типів через FederatedTypesPlugin
- Документація з розгортання та підтримки
- Навчання команди роботі з мікрофронтендами
Яких результатів можна очікувати?
Після впровадження Module Federation ви отримуєте: зниження часу завантаження на 30-50%, незалежні цикли деплою для кожного remote, та зменшення конфліктів при розробці. В одному з проектів ми скоротили середній розмір завантажуваного JS з 1.2 MB до 680 KB. Економія на трафіку може досягати значних сум при високій відвідуваності.
Типові помилки при налаштуванні
- Незазначений
singleton: trueдля React — дублювання залежностей. - Відсутність
eager: falseу shared-залежностей — помилка при ініціалізації. - Неправильне налаштування CORS для remoteEntry.
Для роботи remoteEntry на різних доменах необхідно додати заголовки Access-Control-Allow-Origin: * у відповідь сервера, де розміщено remote. У devServer Webpack це робиться через опцію headers. У production — через налаштування веб-сервера або CDN. Це обов'язкова практика.
Замовте впровадження — ми налаштуємо ваш проект за 5–10 робочих днів. Отримайте консультацію — зв'яжіться з нами для безкоштовного аудиту архітектури.







