Что мы должны были достичь

Почти год назад мы начали работать над новым продуктом в Shipup, нашим продуктом Feedback. Эта функция помогает нашим клиентам отправлять опросы удовлетворенности прямо при доставке. Это позволяет клиенту получить простой вопрос, встроенный в электронное письмо о доставке, которое перенаправляет на веб-страницу, где он может оставить отзыв о доставке. Цель состоит в том, чтобы позволить нашим клиентам создавать настраиваемую функцию обратной связи, которая настраивается непосредственно на нашей платформе. Затем продавец может настроить брендинг, вопросы, активировать веб-перехватчики после ответа и многое другое.

Чтобы обеспечить наилучшую настройку, нам нужно было предоставить им предварительный просмотр их изменений, то есть то, что потребитель увидит, отвечая на опрос.

Решения, которые у нас были

Тем не менее, у нас есть несколько решений для реализации предварительных версий на нашей платформе.

  • Дублирование кода

Самым простым решением, но не самым умным по понятным причинам было дублирование кода. В этом случае, если мы внесем изменения в наш отзыв, нам придется поддерживать код в двух местах: одно для опроса и одно для предварительного просмотра на нашей платформе, что быстро превращается в кошмар разработчика.

  • Пакет NPM

Другим решением было бы представить наши отзывы в виде библиотеки и сделать их зависимыми от нашей платформы. В этом случае, если вы обновляете код, вам нужно опубликовать новую версию библиотеки, добавить версию в платформу и выпустить новую версию платформы. Поскольку мы работаем с полирепозиторием в Shipup, настроить это решение не так просто, как с монорепозиторием. Тем не менее, вы добавите разработчику дополнительную работу для обновления нашей кодовой базы. Более того, вам необходимо убедиться, что вы развертываете оба приложения, Feedback и нашу платформу. В идеальном мире вы должны иметь возможность обновлять отзывы везде одним щелчком мыши.

  • Федеративный модуль Webpack

Вот где на помощь приходит федерация модулей Webpack. Используя Feedback в качестве федеративного модуля внутри нашей платформы, вы можете выпустить новую версию Feedback, и она будет работать везде, здорово! Если вы не знаете, что такое федеративные модули, вы можете прочитать этот пост https://medium.com/swlh/webpack-5-module-federation-a-game-changer-to-javascript-architecture-bcdd30e02669 от Зак Джексон

Федерация модулей позволяет приложению JavaScript динамически загружать код из другого приложения

Реализация нашего продукта обратной связи в виде федеративного модуля

Нам пришлось выставить компонент React из Feedback, этот компонент предварительного просмотра имеет некоторые отличия от оригинального Feedback, поскольку контекст выполнения отличается. Мы хотели отобразить только этап нашего опроса без какого-либо вызова API и без возможности перехода к опросу.

Затем мы добавили плагин ModuleFederation, чтобы показать этот компонент.

Конфигурация удаленного приложения (обратная связь)

new ModuleFederationPlugin({
  name: 'Feedback',
  filename: 'remoteEntry.js',
  exposes: {
    './Preview': './src/App/Preview',
  },
  // ...
}

Как только продукт Feedback смог предоставить компонент Preview, нам нужно было объявить его как объединенный модуль с нашей платформы. Мы ввели способ протестировать его локально, изменив место загрузки федеративного модуля в зависимости от переменной среды.

Конфигурация хост-приложения (платформа Shipup)

new ModuleFederationPlugin({
  name: 'app',
  remotes: {
    Feedback: process.env.LOCAL_FEEDBACK
    ? 'Feedback@<http://localhost:8081/remoteEntry.js>'
    : 'Feedback@<https://feedback.shipup.co/remoteEntry.js>',
  },
  // ...
}

Загрузите федеративный модуль с нашей платформы

Теперь, когда наш компонент предварительного просмотра доступен как объединенный модуль, достаточно сделать импорт для загрузки нашего предварительного просмотра:

const RemoteFeedback = lazy(() => import('Feedback/Preview'));
function FeedbackPreview() {
  return  <Suspense fallback={<Spinner />}>
            <RemoteFeedback {...someProps} />
          </Suspense>
}

Затем, если вы попытаетесь загрузить свое приложение, вы столкнетесь со следующей ошибкой

Hooks can only be called inside the body of a function component.

https://reactjs.org/warnings/invalid-hook-call-warning.html

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

  • Создайте файл bootstrap.js и переместите в него все, что у вас есть из вашего index.js как для удаленных, так и для хост-приложений.
    Ваш файл index.js будет выглядеть следующим образом:
import('./bootstrap');
  • Объявите react и react-dom как общие модули в плагине объединения модулей:
shared: {
  'react': {
      singleton: true,
  },   
  'react-dom': {     
      singleton: true,
  },
}

Если вы хотите получить дополнительные пояснения, вы можете погрузиться в документацию по веб-пакету.

Затем, если вы попытаетесь перезагрузить приложение, вы столкнетесь со второй проблемой:

No required version specified and unable to automatically determine one. Unable to find required version for "react" in description file

Вам нужно будет указать, какую версию использовать, обновив конфигурацию.

shared: {
  'react': {
     singleton: true,
     requiredVersion: '^17.0.0',
  },
  'react-dom': {
     singleton: true,
     requiredVersion: '^17.0.0',
  },
},

Если вы перезагрузите свое приложение, оно, наконец, заработает как шарм!

Таким образом, теперь мы уверены, что предварительный просмотр отзывов всегда отражает то, что увидит потребитель, без каких-либо отклонений.

Где мы сейчас

Прошло 6 месяцев с тех пор, как мы выпустили наш новый продукт Feedback, и мы довольны своим выбором, мы смогли выпустить несколько версий без каких-либо проблем.

Недавно мы сделали еще один шаг вперед, поделившись нашей системой проектирования в виде федеративных модулей и создав наш плагин ShipupFederationPlugin поверх модуля ModuleFederationPlugin.

Этот плагин представляет такие интересные вещи, как:

  • Автоматическое обнаружение локального объединенного модуля в рабочей среде, когда ваш объединенный модуль запускается локально.
  • Управление версиями как запасной вариант на случай, если один проект не может использовать последнюю версию объединенного модуля.
  • Возможность доступа к федеративному модулю с помощью строки запроса из ветки, отправленной на Github, для обеспечения беспрепятственного контроля качества. Разработчику нужно только поделиться конкретным URL-адресом, и он и все, кто участвует в процессе, могут протестировать его улучшения.
  • Управляйте несколькими средами (локальными/постановочными/производственными) для загрузки федеративных модулей из соответствующей среды. Например, когда вы находитесь в промежуточной среде, она также будет загружать федеративные модули из промежуточной среды.

Но это может стать темой отдельного поста…

Хотите начать новый вызов на Shipup? Мы нанимаем! Ознакомьтесь с нашими вакансиями на сайте careers.shipup.co