Налаштування Module Federation (Webpack) для мікрофронтенду

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Налаштування Module Federation (Webpack) для мікрофронтенду
Складна
~3-5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Налаштування Module Federation (Webpack) для мікрофронтендів

Module Federation — вбудований механізм Webpack 5 для спільного використання коду між незалежними збірками в рантаймі. Кожен додаток (remote) публікує частину своїх модулів, інший (host) завантажує їх динамічно без перебудови. Код розгортається незалежно — зміни в remote негайно доступні в host.

Це не iframe та не web components — це справжній JS-код, що розділяє залежності (React, ReactDOM тощо) через механізм shared.

Що входить у роботу

Архітектурне проектування розбиття на remotes, налаштування ModuleFederationPlugin в кожному додатку, типізація через @module-federation/typescript, shared-залежності, динамічне завантаження, обробка помилок завантаження, CI/CD з незалежним розгортанням.

Архітектура

host (shell)          — основний додаток, точка входу
  ├── remote: catalog    — каталог продуктів
  ├── remote: checkout   — оформлення замовлення
  ├── remote: profile    — особистий кабінет
  └── remote: auth       — віджет авторизації (shared UI)

Кожен remote — окремий репозиторій з окремим CI/CD pipeline.

Встановлення

# в кожному додатку
npm install webpack@5 webpack-cli webpack-dev-server
npm install @module-federation/typescript
npm install html-webpack-plugin babel-loader @babel/core @babel/preset-react @babel/preset-typescript

webpack.config.js — Host (shell)

// apps/shell/webpack.config.js
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.example.com'
            : 'http://localhost:3001'
        }/remoteEntry.js`,
        checkout: `checkout@${
          argv.mode === 'production'
            ? 'https://checkout.example.com'
            : 'http://localhost:3002'
        }/remoteEntry.js`,
        auth: `auth@${
          argv.mode === 'production'
            ? 'https://auth.example.com'
            : '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,
  },
})

webpack.config.js — Remote (catalog)

// apps/catalog/webpack.config.js
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', // точка входу для host
      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,
    // CORS — host на іншому порту
    headers: { 'Access-Control-Allow-Origin': '*' },
    historyApiFallback: true,
  },
})

Bootstrap-паттерн

Module Federation потребує асинхронного завантаження. Точка входу має бути асинхронною:

// 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 у host

// apps/shell/src/App.tsx
import React, { Suspense, lazy } from 'react'
import { Routes, Route } from 'react-router-dom'

// TypeScript не знає про remote-модулі без оголошень
const ProductList = lazy(() => import('catalog/ProductList'))
const ProductDetail = lazy(() => import('catalog/ProductDetail'))
const Checkout = lazy(() => import('checkout/CheckoutFlow'))

function App() {
  return (
    <Routes>
      <Route
        path="/products"
        element={
          <Suspense fallback={<PageSkeleton />}>
            <ProductList />
          </Suspense>
        }
      />
      <Route
        path="/products/:id"
        element={
          <Suspense fallback={<PageSkeleton />}>
            <ProductDetail />
          </Suspense>
        }
      />
      <Route
        path="/checkout"
        element={
          <Suspense fallback={<PageSkeleton />}>
            <Checkout />
          </Suspense>
        }
      />
    </Routes>
  )
}

TypeScript-оголошення для remote-модулів

npm install @module-federation/typescript
// webpack.config.js (remote)
const { FederatedTypesPlugin } = require('@module-federation/typescript')

plugins: [
  new ModuleFederationPlugin({ ... }),
  new FederatedTypesPlugin({
    federationConfig: {
      name: 'catalog',
      exposes: { './ProductList': './src/components/ProductList' },
    },
  }),
]
// webpack.config.js (host)
plugins: [
  new ModuleFederationPlugin({ ... }),
  new FederatedTypesPlugin({
    federationConfig: {
      name: 'shell',
      remotes: { catalog: 'catalog@...' },
    },
  }),
]

Типи генеруються автоматично та доступні як @mf-types/catalog/ProductList.

Обробка помилок завантаження remote

// components/RemoteComponent.tsx
import React, { Suspense, Component, ReactNode } from 'react'

interface ErrorBoundaryState { hasError: boolean; error?: Error }

class RemoteErrorBoundary extends Component<
  { fallback: ReactNode; children: ReactNode },
  ErrorBoundaryState
> {
  state: ErrorBoundaryState = { hasError: false }

  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error }
  }

  render() {
    if (this.state.hasError) return this.props.fallback
    return this.props.children
  }
}

export function RemoteComponent({
  component: Component,
  fallback,
  errorFallback,
  ...props
}: {
  component: React.ComponentType<unknown>
  fallback: ReactNode
  errorFallback: ReactNode
  [key: string]: unknown
}) {
  return (
    <RemoteErrorBoundary fallback={errorFallback}>
      <Suspense fallback={fallback}>
        <Component {...props} />
      </Suspense>
    </RemoteErrorBoundary>
  )
}

Обмін даними між мікрофронтендами

Remote-модулі ізольовані — немає спільного Redux/Zustand. Паттерни для комунікації:

Custom Events:

// catalog remote — публікує подію
window.dispatchEvent(new CustomEvent('catalog:add-to-cart', {
  detail: { productId, quantity }
}))

// checkout remote — слухає
window.addEventListener('catalog:add-to-cart', (e: CustomEvent) => {
  checkoutStore.addItem(e.detail)
})

Спільний стан через shared-модуль:

// експонуємо стор з auth remote
exposes: {
  './store': './src/store/authStore',
}

// shared: singleton, щоб всі remotes отримали один екземпляр
shared: {
  './src/store/authStore': { singleton: true }
}

CI/CD — незалежне розгортання

# .github/workflows/catalog.yml
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.example.com --delete
      - name: Invalidate CloudFront
        run: aws cloudfront create-invalidation --distribution-id $CF_ID --paths "/*"

Host отримує оновлений remote без власного розгортання — при наступному завантаженні сторінки.

Що ми робимо

Проектуємо границі між мікрофронтендами, налаштовуємо webpack з ModuleFederationPlugin в кожному додатку, вирішуємо питання shared-залежностей (React singleton, design system), налаштовуємо TypeScript-оголошення, реалізуємо обробку помилок завантаження remote, вистроюємо CI/CD під незалежне розгортання кожного remote.

Строк: 5–10 днів залежно від кількості remotes та наявності монорепозиторію.