Pull to refresh

Comments 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 видит их, как нативный 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. Хромают отступы (как и во всех примерах) - 1, 2, 3 пробела вперемешку. Сложно читать код.

  2. tag.split('-').join('_') - компактный, но очень неэффективный по скорости и памяти способ замены символов. Не надо привыкать так писать, tag.replace(/-/g, '_') ничем не хуже.

  3. Код arr.reduce((object, item) => ({...object, key: value}), {}) имеет сложность O(N^2), где N - размер исходного массива (на каждой итерации происходит shallow clone объекта, в который добавляются ключи). Опять же не надо привыкать так писать код: на малых размерах это терпимо, но если так писать везде - рано или поздно попадётся большой массив.

1) отступы, действительно, в некоторых местах хромают
особенно поломались в предпоследнем примере кода

про 2 и 3: я был больше сосредоточен на результате работы этих функций, и когда пишешь статью, то глаз уже замыливается ))

в целом, спасибо за комментарий

Sign up to leave a comment.