Настройка 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 для sharing кода между независимыми сборками в рантайме. Каждое приложение (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 pattern

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>
  )
}

Обмен данными между microfrontends

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 state через 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 без своего деплоя — при следующей загрузке страницы.

Что делаем

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

Срок: 5–10 дней в зависимости от количества remotes и наличия монорепозитория.