Механизм shared в Module Federation решает задачу централизованного и
согласованного использования общих зависимостей между независимыми
сборками (host и remote). Основная цель — избежать дублирования
библиотек, конфликтов версий и множественной инициализации глобального
состояния, сохраняя при этом автономность микрофронтендов.
Shared-зависимости работают поверх runtime-слоя Webpack и опираются
на концепцию share scope — именованного пространства, в
котором регистрируются и резолвятся общие модули.
Share
Scope: механизм рантайм-изоляции и совместного доступа
Webpack использует runtime-слой для управления общими модулями через
глобальные области видимости.
Основная логика:
- каждый контейнер (host или remote) может
публиковать зависимости в share scope
- каждый контейнер может потреблять зависимости из
share scope
- зависимости хранятся не как обычные import-ы, а как
runtime-реестр
Ключевые характеристики:
- share scope существует в рантайме браузера
- инициализация происходит через
__webpack_init_sharing__
- загрузка модулей выполняется асинхронно
- возможен fallback на локальную версию
Конфигурация
shared в ModuleFederationPlugin
Базовая структура:
new ModuleFederationPlugin({
name: 'app',
remotes: {},
exposes: {},
shared: {}
})
shared может задаваться в двух формах:
Строковая форма
shared: {
react: '18.2.0',
'react-dom': '18.2.0'
}
Это упрощённый вариант, где версия интерпретируется как минимально
требуемая.
Объектная форма
shared: {
react: {
singleton: true,
requiredVersion: '^18.2.0',
eager: false,
strictVersion: true
}
}
Позволяет управлять поведением зависимости на уровне
runtime-резолва.
Ключевые параметры
shared-зависимостей
singleton
Гарантирует, что в runtime будет использоваться только одна
копия модуля.
react: {
singleton: true
}
Поведение:
- первая загруженная версия становится активной
- остальные запросы переиспользуют её
- критично для библиотек с глобальным состоянием
Типичные кандидаты:
- react
- react-dom
- vue (в некоторых архитектурах)
- state managers (redux, zustand)
requiredVersion
Определяет допустимый диапазон версий.
requiredVersion: '^18.0.0'
Webpack сравнивает:
- версию, предоставленную remote
- версию, доступную в host
Если несовместимость — поведение зависит от
strictVersion.
strictVersion
strictVersion: true
При включении:
- несовпадение версии приводит к runtime-ошибке
- fallback отключается
При выключении:
- Webpack может использовать ближайшую подходящую версию
- допускается деградация
eager
eager: true
Заставляет загрузить зависимость синхронно на этапе
инициализации контейнера.
Особенности:
- уменьшает количество async boundary
- увеличивает размер initial bundle
- может вызвать конфликт, если dependency не доступна сразу
Используется редко, в основном для критических библиотек.
import
Позволяет задать альтернативный модуль, который будет использоваться
как fallback:
react: {
import: false
}
или:
react: {
import: 'preact/compat'
}
Применяется для замены реализаций библиотек.
shareKey
Позволяет переопределить имя ключа в share scope:
'@lib/utils': {
shareKey: 'utils'
}
Используется для унификации разных путей к одному пакету.
shareScope
Указывает область совместного использования:
shareScope: 'default'
По умолчанию все используют default, но возможно
создание изолированных scope для:
- A/B тестов
- multi-tenant приложений
- версионной изоляции
Алгоритм разрешения
shared-зависимостей
Runtime Webpack выполняет несколько шагов:
- Проверка наличия share scope
- Поиск зарегистрированных версий модуля
- Сравнение версий через semver
- Проверка singleton-ограничений
- Выбор наиболее подходящей версии
- При отсутствии — загрузка локального fallback
Псевдологика:
- если singleton → выбрать первую и закрепить
- если strictVersion → только точное совпадение
- иначе → выбрать максимальную совместимую версию
Поведение host и remote при
shared
Host
- инициализирует share scope
- регистрирует локальные зависимости
- может переопределять remote зависимости
Remote
- подключается к существующему share scope
- пытается использовать уже загруженные зависимости
- при отсутствии — загружает собственные версии
Проблемы множественных
копий библиотек
Наиболее частая ошибка в Module Federation связана с дублированием
React.
Пример проблемы:
- host использует React 18.2
- remote загружает React 18.1
- в результате создаются две runtime-копии React
Последствия:
- ломаются hooks
- контекст React перестаёт работать
- появляется ошибка invalid hook call
Решение:
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true }
}
Поведение версий и semver
Webpack использует semver-сравнение:
- ^18.2.0 → разрешает 18.x.x
- ~18.2.0 → разрешает только патчи
- exact version → строгое совпадение
При конфликте:
- выбирается highest compatible version
- либо fallback на local module
Оптимизация загрузки
shared-зависимостей
Предзагрузка критических
библиотек
shared: {
react: { eager: true }
}
Снижает latency при первом render, но увеличивает initial
payload.
Lazy resolution
По умолчанию:
- shared modules загружаются асинхронно
- подгружаются только при первом использовании
Tree-shaking взаимодействие
Shared-зависимости могут ухудшать tree-shaking:
- модуль загружается как единый chunk
- оптимизация внутри него ограничена
Monorepo и
shared-зависимости
В монорепозиториях shared часто пересекается с
workspace-резолвингом.
Особенности:
- версии библиотек могут быть физически одинаковыми
- Webpack всё равно выполняет runtime проверку
- singleton становится обязательным для UI-библиотек
Конфликты и их причины
1. Несовпадение React
экземпляров
Причина:
- разные node_modules у host и remote
2. Разные версии
peerDependencies
Причина:
- несогласованность package.json
3. eager + async remote
Причина:
- попытка синхронного доступа к ещё не загруженному remote
Продвинутые сценарии
использования shared
Замена библиотек (alias
через import)
shared: {
lodash: {
import: 'lodash-es'
}
}
Позволяет централизованно менять реализацию.
Изоляция по shareScope
shareScope: 'admin'
shareScope: 'public'
Используется для разделения сред внутри одного приложения.
Версионирование
микрофронтендов
Shared может использоваться как механизм мягкой миграции:
- старая версия остаётся fallback
- новая версия постепенно захватывает scope
- переход без полного деплоя всех remote
Runtime-инварианты
shared-модели
- один share scope может содержать несколько версий одного пакета
- singleton гарантирует только одну активную версию
- strictVersion управляет жёсткостью проверки
- eager изменяет фазу загрузки, но не поведение резолва
- final выбор версии всегда происходит в runtime, а не в build
time
Производственные ограничения
- увеличение complexity runtime логики
- сложность дебага зависимостей
- необходимость синхронизации версий между командами
- зависимость от корректного semver в экосистеме
Типовые конфигурации shared
Базовая стабильная
конфигурация
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true },
axios: {}
}
Строгая корпоративная
конфигурация
shared: {
react: { singleton: true, strictVersion: true },
'react-dom': { singleton: true, strictVersion: true },
'@company/ui': { singleton: true }
}
Производительная
конфигурация с eager
shared: {
react: { singleton: true, eager: true },
'react-dom': { singleton: true, eager: true }
}