Ви запускаєте кілька 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 робочих днів. Отримайте консультацію — зв'яжіться з нами для безкоштовного аудиту архітектури.







