Что такое Module Federation в Webpack и зачем он нужен?
Идея и назначение
Module Federation (MF) — фича Webpack 5, которая позволяет нескольким независимо собранным JS-приложениям подключать модули друг из друга во время выполнения, а не на этапе сборки. Это база для микрофронтендов: разные команды пишут, собирают и деплоят свои приложения отдельно, а в браузере они объединяются в единое целое.
Host и Remote
- Remote — приложение, которое отдаёт свои модули наружу через
exposes. - Host — приложение, которое подключает чужие модули через
remotes. - Одно и то же приложение может одновременно быть и host, и remote (bidirectional federation), что типично для сеток микрофронтендов.
Конфигурация
Всё настраивается через ModuleFederationPlugin: name — имя контейнера, filename — обычно remoteEntry.js, exposes — какие модули отдаём, remotes — откуда берём чужие модули, shared — общие зависимости (react, react-dom и т.д.), чтобы не тащить их дублями в разные бандлы.
Как это работает под капотом
При сборке remote генерирует специальный файл-контейнер (remoteEntry.js) — runtime-обёртку с реестром модулей и их асинхронными загрузчиками. Host в рантайме подгружает этот скрипт динамическим <script>-тегом, получает глобальный объект-контейнер, согласовывает shared-скоуп (сравнение версий общих зависимостей по semver) и запрашивает нужный модуль через container.get('./Module'). Импорт всегда асинхронный, поэтому используется динамический import(), обычно обёрнутый в React.lazy + Suspense.
Shared-зависимости
Поле shared решает проблему дублирования библиотек. Если host и remote оба используют react, объявляем её как shared с singleton: true, чтобы в рантайме работал один инстанс — это критично для контекста, хуков и синглтон-состояния. Webpack сверяет версии и решает, чей билд библиотеки грузить, либо выдаёт предупреждение при несовместимости.
Отличия от прежних подходов к интеграции
До MF независимые фронтенды соединяли через iframe (изоляция, но проблемы со стилями/роутингом/производительностью), общие npm-пакеты с компонентами (требуют пересборки и передеплоя при любом изменении) или Single-SPA с ручной загрузкой скриптов. Module Federation даёт нативный для бандлера механизм с поддержкой code splitting, версионирования зависимостей и полностью независимого деплоя частей приложения.
Применение шире микрофронтендов
MF используют и для плагинных архитектур (динамическая загрузка плагинов без пересборки хоста), A/B-тестирования отдельных фич, постепенной миграции legacy-кода на новый стек.
Ограничения и подводные камни
Изначально доступен только в Webpack 5 (позже появились независимые реализации вроде @module-federation/enhanced, портированные на Vite и Rspack). Рассинхрон версий shared-зависимостей — частый источник рантайм-ошибок, которые сборка не ловит. Для TypeScript типы remote-модулей по умолчанию недоступны статически — их генерируют отдельно, например через dts-плагины экосистемы Module Federation.
Что хочет услышать интервьюер
Кандидат объясняет разницу между host и remote и то, что импорт remote-модулей всегда асинхронный
Упоминает поле shared и singleton для избежания дублирования и рассинхрона версий react/react-dom
Понимает, что модули подключаются в рантайме через remoteEntry.js, а не на этапе сборки
Может связать Module Federation с архитектурой микрофронтендов и независимым деплоем
Знает про ограничения: версии зависимостей, отсутствие типов из коробки, привязку к Webpack 5
Пример: webpack.config.js remote-приложения
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ...
plugins: [
new ModuleFederationPlugin({
name: 'shop',
filename: 'remoteEntry.js',
exposes: {
// отдаём компонент наружу под именем './ProductList'
'./ProductList': './src/components/ProductList',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
};
Пример: webpack.config.js host-приложения
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ...
plugins: [
new ModuleFederationPlugin({
name: 'app',
remotes: {
// shop — локальное имя, адрес remoteEntry.js отдельного деплоя
shop: 'shop@https://shop.example.com/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
};
Пример: Использование remote-модуля в host
import { lazy, Suspense } from 'react';
// импорт всегда асинхронный — модуль подгружается по сети в рантайме
const ProductList = lazy(() => import('shop/ProductList'));
function App() {
return (
<Suspense fallback={<div>Загрузка...</div>}>
<ProductList />
</Suspense>
);
}
Типичные ошибки
Путают Module Federation с обычным code splitting или dynamic import внутри одного приложения
Забывают указать shared-зависимости или singleton, из-за чего в рантайме грузится несколько копий react
Считают, что импорт remote-модуля синхронный и не оборачивают его в Suspense/lazy
Не могут объяснить, что такое remoteEntry.js и как host находит и инициализирует container
Игнорируют проблему версионирования и деплоя remotes — не учитывают, что remote может обновиться независимо и сломать host


