Комментарии 12
Спасибо за статью!
Тоже разбирался с микрофронтами и module federation. Была задача сделать загрузку микрофронтов таким образом, чтобы не приходилось ничего хардкодить. При твоём подходе на самом деле нет особой разницы между статикой и динамикой, потому что всё упирается в то, что список микрофронтов и ссылки на их remoteEntry зашиты в вебпак конфиге, из-за чего без новой сборки и нового деплоя ядра системы выкатить новый модуль не получится.
Я нашел такую статью по использованию федерации модулей в angular: https://www.angulararchitects.io/en/aktuelles/dynamic-module-federation-with-angular/
Там описано использование либы \@angular-architects/module-federation. Это комплексная библиотека, из которой нужна только одна из ее зависимостей - \@angular-architects/module-federation-runtime. В ней вполне себе фреймворконезависимый код, который предоставляет возможность контролировать загрузку модулей прямо из кода ядра системы, а не из конфига вебпака. Если смущает \@angular-architects в то время, когда используется, скажем, React, всегда можно сделать свою внутреннюю библиотеку:D В итоге задача сводится к написанию функции, которая на вход получает конфиг со списком модулей (это может быть json-файлик, какой-то бэкенд метод) и возвращает тебе модули, загруженные функцией loadRemoteModule. Я поэкспериментировал с React, всё работает как часы.
Надеюсь, будет полезно, много шишек набил на этой федерации модулей)
P.S. жаль пока нет адекватного способа завести MF на SSR.
да, согласен
у меня не было цели создать максимально гибкий инструмент для работы из "коробки"
предварительно, мы с беком и другими командами определили по какому адресу будут храниться микрофронтенды и где забирать build-tag
это необходимый этап, после которого нужно один раз описать эту логику, а затем разработчики просто пишут список удаленных модулей
спасибо за ссылку на angular-architects, изучу его подробнее
Как IDE видит подобные модули?
Нет ли ругани со стороны es-lint?
IDE и es-lint видит их, как нативный js module,
поэтому с этим проблем нет
или я пока не заметил
может есть конкретный пример ругани es-lint ?
Если монорепа, можно в ts config указать "module_name/path": "path_to_component"
Если несколько разных реп, то либо в ядре надо руками прописывать declare "module_name" {}, либо в самом модуле надо при сборке генерировать типы и упаковывать в либу, после чего в ядре тянуть их как dev deps. Других вариантов не существует. Сначала я думал, что это очень плохо, что мы не знаем тип компонента и его пропсы, но позже я пришел к мысли, что оно и не нужно, потому что в идеале не должно быть никаких параметров. Типа куском модуль подцепляем и всё, делая его максимально независимым.
А чем это удобнее, чем динамически вставлять <script>
со ссылкой на нужный бандл?
Адрес можно брать json, сгенеренный тем же webpack.
Не сочтите за троллинг, но из статьи действительно не понятно в чем тут простота.
шаринг зависимостей, например
Мне кажется, стоит об этом упомянуть в тексте, чтобы было понятно, для чего вообще весь этот оверхед с настройкой module federation нужен.
в разделе "Динамические удалённые модули", я объяснил для каких целей мы используем в компании динамическую загрузку
в целом, никто не запрещает писать инструмент с нуля)
мне было интересно реализовать это с помощью ModuleFederation, поскольку он максимально гибкий и не привязан к конкретному фреймворку
главная причина - это необходимость асинхронной операции, в которой мы получаем последнюю опубликованную версию пакета
плагин ModuleFederation это и предоставляет, правда в неудобном виде и поэтому я решил его доработать
возможно не понял вопрос, но как вы планируете сами динамически вставлять "нужный" бандл?
плагин ModuleFederation делает за нас эту работу
предварительно он запрашивает входную точку (remoteEntry.js)
, добавляя <script>
с ним в <head>
затем remoteEntry.js
импортирует запрашиваемый бандл, так же добавляя его в <head>
при этом удаленный бандл может быть поделен также на несколько чанков, которые будут загружаться по необходимости
За статью в целом спасибо, но в частности не понравился пример кода
modules.reduce((object, remoteModule) => {
const remoteName = remoteModule.tag.split('-').join('_');
return {
...object,
[remoteName]: promise ${getRemoteModule(remoteName, remoteModule.url)},
}
}, {})
Хромают отступы (как и во всех примерах) - 1, 2, 3 пробела вперемешку. Сложно читать код.
tag.split('-').join('_')
- компактный, но очень неэффективный по скорости и памяти способ замены символов. Не надо привыкать так писать,tag.replace(/-/g, '_')
ничем не хуже.Код
arr.reduce((object, item) => ({...object, key: value}), {})
имеет сложность O(N^2), где N - размер исходного массива (на каждой итерации происходит shallow clone объекта, в который добавляются ключи). Опять же не надо привыкать так писать код: на малых размерах это терпимо, но если так писать везде - рано или поздно попадётся большой массив.
Module Federation: простая загрузка динамических модулей