Подробное руководство по совместному использованию компонентов React между приложениями с помощью Webpack 5.

вступление

Несколько статей и видеороликов пытаются объяснить, как совместно использовать компоненты с помощью Webpack 5, но, по моему скромному мнению, они допускают несколько критических ошибок: им трудно следовать, они используют сложную терминологию и описывают решения очень абстрактно.

И поэтому вы этого не понимаете и идете дальше, не имея возможности легко расшарить компоненты между вашими приложениями!

Итак, давайте немного повеселимся с Module Federation Webpack 5. Как всегда, мы подробно опишем все тонкости. И скорее всего будут красивые скриншоты.

Примечание. Фрагменты кода ниже взяты из двух небольших приложений React, которые я написал специально для этой статьи. Вы можете найти код на GitLab:

Магазин велосипедов Джерарда и Блог Джерарда.

Давайте двигаться дальше!

Цель

Мы создали небольшой магазин с React, и он использует Webpack 5 в качестве сборщика. У нас также есть небольшой блог, созданный с помощью React.

Да, вы уже догадались: он также использует Webpack 5 в качестве сборщика.

Внутри магазина у нас есть компонент React, отображающий последнее предложение. Но мы также хотим сделать это внутри блога! Это будет нашей целью. Мы хотим поделиться компонентом.

Во-первых, мы подробнее рассмотрим оба приложения.

Магазин

Пожалуйста, восхищайтесь магазином. Справедливости ради, там не так много всего происходит, но есть один приятный компонент. Компонент LatestOffer. Как вы можете видеть, он отображает изображение и некоторый текст с некоторыми причудливыми цветами. Все, что находится внутри оранжевой рамки (и сама граница), является компонентом LatestOffer. Он отображается в строке 8 внутри BicycleShop.jsx, как вы можете видеть ниже:

Сам компонент определен в отдельном файле LatestOffer.jsx:

Обратите внимание, как мы используем изображение и некоторые стили в нашем компоненте. Мы выбрали использование модулей CSS, но это необязательно.

И вот оно. Небольшой магазин с компонентом LatestOffer.

Что насчет другого приложения?

Блог

Помимо магазина, у нас есть еще одно автономное приложение React для простого блога. Внутри этого блога, в области, обведенной пунктирной линией, мы хотим отобразить… последнее предложение нашего магазина. Что и является нашей целью, как мы упоминали ранее.

Мы знаем, что компонент LatestOffer существует, но он определен в репозитории Shop, а не в нашем блоге.

Так как же нам разделить компонент между этими приложениями? Как мы делимся компонентом из магазина в блог? Вот где в игру вступает Module Federation Webpack 5. Он обладает мощной функциональностью, но его трудно понять, когда вы исследуете его.

Итак, давайте двигаться дальше и делать маленькие шаги за раз.

Объединение с Webpack

Оба приложения (магазин и блог) имеют схожие настройки для связывания кода. Они оба используют Webpack, и их конфигурация по большей части очень идентична.

Они оба имеют одинаковую конфигурацию module-rules внутри своего файла webpack.config.js, как вы можете видеть ниже:

Это обеспечивает правильную загрузку и объединение таблиц стилей и файлов JavaScript/JSX. Но это не руководство по настройке Webpack в целом, поэтому мы оставим эти правила на другой раз.

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

Федерация модулей: раскрытие компонентов

Наша цель — поделиться LatestOfferкомпонентом из приложения магазина. Поэтому мы сначала изменим файл webpack.config.js приложения магазина, добавив экземпляр файла ModuleFederationPlugin . Вот как начинается вся магия (см. строки 11–17):

Мы передаем объект options в конструктор:

  • name (строка 12): это определяет имя для текущего проекта (помните, что в данный момент мы редактируем файл конфигурации Webpack для проекта Shop). Имя произвольное, как и в верхнем регистре.
  • filename (строка 13): см. следующий абзац.
  • exposes (строки 14–16): Здесь вы определяете, какие компоненты вы хотите выставить, и под каким именем они будут доступны (подробнее об этом позже). В этом примере мы раскрываем компонент LatestOffer, давая ему имя (./LatestOffer — обратите внимание на ведущую точку и косую черту. И это может быть любое имя, но мы решили использовать одно и то же имя) и определяя путь к файл определения компонента в виде строки (./src/components/LatestOffer/LatestOffer). Излишне говорить, что мы могли бы выставить несколько компонентов таким образом, если бы захотели.

Так что насчет имени файла, remoteEntry.js?

Другие приложения, которые хотят использовать наш открытый компонент LatestOffer, каким-то образом должны получить определение компонента (то есть код для компонента). Другими словами, они должны иметь возможность загрузить этот код.

Предположим, что наше приложение магазина работает по адресу localhost:8080 (что, по совпадению, и есть).

Настроив имя файла remoteEntry.js (опять же, произвольное значение), мы создали маршрут localhost:8080/remoteEntry.js. И вы можете догадаться, что там сейчас можно найти — код для нашего компонента LatestOffer (среди некоторого другого шаблонного кода):

Теперь мы выставили наш компонент. Мы разделяем его из нашего приложения магазина.

Давайте посмотрим, как мы можем потреблять его с другой стороны. Как мы можем визуализировать компонент LatestOffer внутри приложения блога.

Объединение модулей: удаленные компоненты

Мы идем дальше и сейчас рассмотрим файл конфигурации Webpack для приложения блога. Опять же, как мы только что сделали для конфигурации Webpack магазина, мы добавим экземпляр ModuleFederationPlugin с некоторыми параметрами:

Параметры, которые мы передаем конструктору:

  • name (строка 12): Опять же, имя текущего проекта.
  • remotes (строки 13–15): По сути, мы говорим нашему приложению: «Привет! Есть еще одно приложение под названием SHOP, и оно предоставляет доступ к компонентам! Давайте дадим себе возможность загрузить их в наш проект!». Ключ объекта, SHOP, — это то, как мы можем локально обращаться к внешним компонентам (подробнее об этом позже). А значение представляет собой строку, состоящую из двух частей, соединенных знаком at. Первая часть — это имя удаленного приложения, а вторая часть — это файл, содержащий код загружаемых нами компонентов (теперь это должно иметь некоторый смысл).

Наконец, пришло время отрисовать компонент LatestOffer в нашем блоге.
Давайте посмотрим.

Визуализация удаленных компонентов

Взгляните на компонент Blog, который рендерит компонент LatestOffer:

Посмотрите, как мы получаем ссылку на компонент LatestOffer (строка 3).

Мы используем так называемый динамический импорт. Функция React lazy позволяет нам сделать это. Это сложная тема, и вы можете прочитать о ней больше в Документации React.

После загрузки (удаленного!) компонента рендерим его (строки 10–12). Обратите внимание, что мы заключаем компонент в компонент suspenseHigher-Order (HOC) с запасным значением. Резервное значение будет отображаться, пока мы загружаем наш удаленный компонент (если он еще не был загружен). После загрузки компонент LatestOffer отображается внутри нашего блога!

Заключительные слова

На мой взгляд, модульная федерация Webpack 5 немного затянулась, и это очень жаль. Эта статья доказывает, насколько она мощна — и поверьте мне, мы только коснулись поверхности.

Но некоторые вещи не работали в пользу Module Federation:

Плохое название

Услышав термин «Федерация модулей», большинству разработчиков не сразу становится ясно. Представьте, что плагин Webpack вместо этого назывался «ShareComponentsPlugin».

Страшная терминология

Чаще всего концепция микроинтерфейсов затрагивается в руководствах по федерации модулей. Я убежден, что вы уже потеряли как минимум половину своей аудитории, пытаясь использовать такую ​​мощную и сложную концепцию. Что плохого в том, чтобы сказать: «Эй! Вы хотите обмениваться компонентами между приложениями? Вот как!"

Затем сами разработчики обнаружат, что вместо того, чтобы делиться простым презентационным компонентом, они могут фактически поделиться полным приложением (вы это делали, не так ли? Вот небольшой секрет: вы можете! В конце концов, полноценное приложение React — это просто еще один компонент — кто-то сказал App ?)

Плохая документация

Даже Зак Джексон (активно участвующий в поддержке и разработке Webpack и Module Federation) сам говорит, что документацию для Module Federation можно улучшить.

Взгляните, например, на комментарии в этой проблеме Webpack на GitHub.

Но самое главное, модульная федерация чрезвычайно мощная. Небо это предел.

Попробуйте! Возможно, вскоре вы вместе со своими коллегами будете поддерживать следующее приложение Web 4.0, управляемое микроинтерфейсом.

Спасибо за ваше время!