Сильные стороны и преимущества микро-фронтендов

Опубликовано: 2022-03-11

Микро-интерфейсная архитектура — это подход к проектированию, при котором интерфейсное приложение разбивается на отдельные полунезависимые «микроприложения», свободно работающие вместе. Концепция микроинтерфейса смутно вдохновлена ​​микросервисами и названа в их честь.

К преимуществам шаблона микро-интерфейса относятся:

  1. Архитектуры микроинтерфейса могут быть проще, и, следовательно, их легче анализировать и управлять ими.
  2. Независимые команды разработчиков могут легче сотрудничать в интерфейсном приложении.
  3. Они могут предоставить средства для миграции из «старого» приложения, запустив параллельно с ним «новое» приложение.

Хотя микрофронтендам в последнее время уделяется много внимания, пока нет ни одной доминирующей реализации, ни четкого «лучшего» фреймворка для микрофронтенда. На самом деле существует множество подходов в зависимости от целей и требований. См. библиографию для некоторых из наиболее известных реализаций.

В этой статье мы пропустим большую часть теории микрофронтендов. Вот что мы не будем освещать:

  • «Разделение» приложения на микроприложения
  • Проблемы развертывания, в том числе то, как микроинтерфейсы вписываются в модель CI/CD.
  • Тестирование
  • Должны ли микроприложения согласовываться один к одному с микросервисами на серверной части
  • Критика концепции микро-фронтенда
  • Разница между микрофронтендом и простой старой компонентной архитектурой

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

Наша реализация называется Yumcha. Буквальное значение «ням ча» на кантонском диалекте — «пить чай», но его повседневное значение — «выйти за димсам». Идея заключается в том, что отдельные микроприложения внутри макроприложения (так мы будем называть составленное приложение верхнего уровня) аналогичны различным корзинам с небольшими порциями, приготовленными на обеде димсам.

Обзорная иллюстрация примера приложения на основе микроинтерфейса, как описано выше.

Иногда мы будем называть Yumcha «микроинтерфейсной структурой». В современном мире термин «фреймворк» обычно используется для обозначения Angular, React, Vue.js или других подобных надстроек для веб-приложений. Мы вообще не говорим о фреймворке в этом смысле. Мы называем Yumcha фреймворком просто для удобства: на самом деле это скорее набор инструментов и несколько тонких слоев для создания приложений на основе микрофронтенда.

Учебное пособие по микроинтерфейсу Первые шаги: разметка для составного приложения

Давайте погрузимся и подумаем о том, как мы могли бы определить макроприложение и микроприложения, которые его составляют. Разметка всегда была в основе Интернета. Таким образом, наше макроприложение не будет иметь ничего более сложного, чем следующая разметка:

 <html> <head> <script src="/yumcha.js"></script> </head> <body> <h1>Hello, micro-frontend app.</h1> <!-- HERE ARE THE MICROAPPS! --> <yumcha-portal name="microapp1" src="https://microapp1.example.com"></yumcha-portal> <yumcha-portal name="microapp2" src="https://microapp2.example.com"></yumcha-portal> </body> </html>

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

Мы назвали пользовательский элемент, используемый для микроприложений <yumcha-portal> потому что «портал» — многообещающий термин для микроприложений, используемый в предложении портала, ранней попытке определить стандартный элемент HTML для использования в микроинтерфейсах.

Реализация пользовательского элемента <yumcha-portal>

Как нам реализовать <yumcha-portal> ? Так как это пользовательский элемент, как веб-компонент, конечно! Мы можем выбирать из множества сильных претендентов на написание и компиляцию веб-компонентов микроинтерфейса; здесь мы будем использовать LitElement, последнюю версию Polymer Project. LitElement поддерживает синтаксический сахар на основе TypeScript, который обрабатывает для нас большую часть шаблонов пользовательских элементов. Чтобы сделать <yumcha-portal> доступным для нашей страницы, мы должны включить соответствующий код в виде <script> , как мы сделали выше.

Но что на самом деле делает <yumcha-portal> ? В первом приближении было бы просто создать iframe с указанным источником:

 render() { return html`<iframe src=${this.src}></iframe>`; }

…где render — это стандартный хук рендеринга LitElement, использующий литерал шаблона с тегами html . Этой минимальной функциональности может быть почти достаточно для некоторых тривиальных случаев использования.

Встраивание микроприложений в iframe

iframe — это HTML-элемент, который все любят ненавидеть, но на самом деле они обеспечивают чрезвычайно полезное и надежное поведение в песочнице. Тем не менее, существует длинный список проблем, о которых следует помнить при использовании iframe , которые могут повлиять на поведение и функциональность нашего приложения:

  • Во-первых, у iframe есть хорошо известные особенности с точки зрения их размера и компоновки.
  • CSS, конечно же, будет полностью изолирован от iframe , к лучшему или к худшему.
  • Кнопка «назад» браузера будет работать достаточно хорошо, хотя текущий навигационный статус iframe не будет отражаться в URL-адресе страницы , поэтому мы не могли ни вырезать и вставлять URL-адреса, чтобы получить то же состояние составного приложения, ни глубокие ссылки. им.
  • Связь с iframe извне, в зависимости от нашей настройки CORS, может потребоваться через протокол postMessage .
  • Необходимо будет принять меры для аутентификации через границы iframe .
  • Некоторые программы чтения с экрана могут наткнуться на границу iframe или им нужно, чтобы iframe имел заголовок, который они могут объявить пользователю.

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

Положительным моментом является то, что iframe будет иметь свою собственную независимую Content-Security-Policy (CSP). Кроме того, если микроприложение, на которое указывает iframe , использует сервис-воркер или реализует рендеринг на стороне сервера, все будет работать должным образом. Мы также можем указать различные параметры песочницы для iframe , чтобы ограничить его возможности, например возможность перехода к верхнему фрейму.

Некоторые браузеры добавили или планируют добавить атрибут loading=lazy для элементов iframe , который откладывает загрузку элементов iframe в нижней части экрана до тех пор, пока пользователь не прокрутит их до нужного места, но это не обеспечивает точного контроля отложенной загрузки, который мы хотим.

Настоящая проблема с iframe заключается в том, что для извлечения содержимого iframe потребуется несколько сетевых запросов. index.html верхнего уровня, его сценарии загружены и его HTML-код проанализирован, но затем браузер должен инициировать другой запрос HTML-кода iframe , дождаться его получения, проанализировать и загрузить его сценарии и отобразить содержимое iframe . Во многих случаях JavaScript iframe все равно должен будет раскрутиться, сделать свои собственные вызовы API и показать значимые данные только после того, как эти вызовы API вернутся и данные будут обработаны для просмотра.

Это, вероятно, приведет к нежелательным задержкам и артефактам рендеринга, особенно когда задействовано несколько микроприложений. Если приложение iframe реализует SSR, это поможет, но все же не избавит от необходимости дополнительных циклов.

Таким образом, одна из ключевых проблем, с которыми мы сталкиваемся при разработке реализации нашего портала, заключается в том, как справиться с проблемой кругового пути. Наша цель состоит в том, чтобы один сетевой запрос отключил всю страницу со всеми ее микроприложениями, включая любой контент, который каждое из них может предварительно заполнить. Решение этой проблемы кроется в сервере Yumcha.

Юмча-сервер

Ключевым элементом представленного здесь решения для микроинтерфейса является настройка выделенного сервера для обработки композиции микроприложений. Этот сервер проксирует запросы на серверы, на которых размещено каждое микроприложение. Конечно, для настройки и управления этим сервером потребуются некоторые усилия. Некоторые подходы к микроинтерфейсу (например, single-spa) пытаются обойтись без таких специальных настроек сервера во имя простоты развертывания и настройки.

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

Обратный прокси-сервер, помимо маршрутизации запросов микроприложений на соответствующий сервер, также направляет запросы макроприложений на сервер макроприложений. Этот сервер особым образом подготавливает HTML-код для составленного приложения. Получив запрос index.html от браузера через прокси-сервер по URL-адресу, такому как http://macroapp.example.com , он извлекает index.html , а затем подвергает его простому, но важному преобразованию перед возвратом. Это.

В частности, HTML анализируется на наличие тегов <yumcha-portal> , что можно легко сделать с помощью одного из компетентных парсеров HTML, доступных в экосистеме Node.js. Используя атрибут src для <yumcha-portal> , устанавливается связь с сервером, на котором работает микроприложение, и извлекается его файл index.html , включая содержимое, отображаемое на стороне сервера, если таковое имеется. Результат вставляется в HTML-ответ как <script> или <template> , чтобы браузер не выполнял его.

Иллюстрация серверной архитектуры Yumcha. Браузер обменивается данными с обратным прокси-сервером, который, в свою очередь, взаимодействует с макроприложением и каждым из микроприложений. Шаг macroapp преобразует и предварительно заполняет основной файл index.html приложения.

Преимущества этой настройки заключаются, в первую очередь, в том, что при самом первом запросе index.html для составленной страницы сервер может полностью получить отдельные страницы с отдельных серверов микроприложений, включая контент, обработанный SSR, если any — и доставить в браузер единую полную страницу, включая контент, который можно использовать для заполнения iframe без дополнительных обращений к серверу (используя малоиспользуемый атрибут srcdoc ). Прокси-сервер также гарантирует, что любые сведения о том, откуда обслуживаются микроприложения, скрыты от посторонних глаз. Наконец, это упрощает проблемы с CORS, поскольку все запросы приложений отправляются к одному и тому же источнику.

Вернувшись к клиенту, создается экземпляр тега <yumcha-portal> , который находит содержимое там, где оно было размещено в ответном документе сервером, и в соответствующее время отображает iframe и присваивает содержимое его srcdoc . Если мы не используем iframe (см. ниже), то содержимое, соответствующее этому <yumcha-portal> , вставляется либо в теневую модель DOM пользовательского элемента, если мы ее используем, либо непосредственно в документ.

На данный момент у нас уже есть частично функционирующее приложение на основе микроинтерфейса.

Это лишь верхушка айсберга с точки зрения интересного функционала сервера Yumcha. Например, мы хотели бы добавить функции для управления тем, как обрабатываются ответы об ошибках HTTP от серверов микроприложений, или как поступать с микроприложениями, которые отвечают очень медленно — мы не хотим ждать целую вечность, чтобы обслужить страницу, если одно микроприложение не работает. отвечает! Эти и другие темы мы оставим для другого поста.

Логика преобразования Yumcha macroapp index.html может быть легко реализована в виде бессерверной лямбда-функции или в качестве промежуточного программного обеспечения для серверных сред, таких как Express или Koa.

Управление микроприложениями на основе заглушек

Возвращаясь к клиентской стороне, есть еще один аспект реализации микроприложений, который важен для эффективности, ленивой загрузки и рендеринга без зависаний. Мы могли бы сгенерировать тег iframe для каждого микроприложения либо с атрибутом src , который делает еще один сетевой запрос, либо с атрибутом srcdoc заполненным содержимым, заполненным для нас сервером. Но в обоих этих случаях код в этом iframe запустится немедленно, включая загрузку всех его скриптов и тегов ссылок, начальную загрузку и любые начальные вызовы API и соответствующую обработку данных, даже если пользователь даже не получит доступ к рассматриваемому микроприложению.

Наше решение этой проблемы состоит в том, чтобы сначала представить микроприложения на странице в виде крошечных неактивированных заглушек, которые затем можно будет активировать. Активация может быть вызвана появлением в поле зрения области микроприложения, использованием недостаточно используемого API-интерфейса IntersectionObserver или, чаще, предварительными уведомлениями, отправленными извне. Конечно, мы также можем указать, что микроприложение будет активировано немедленно.

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

 render() { if (!this.activated) return html`{this.placeholder}`; else return html` <iframe srcdoc="${this.content}" @load="${this.markLoaded}"></iframe>`; }

Связь между микроприложениями

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

В соответствии с нашим минималистским мышлением, мы хотим избежать введения большого количества механизмов передачи сообщений. Вместо этого, в духе веб-компонентов, мы будем использовать события DOM. Мы предоставляем тривиальный широковещательный API, который заранее уведомляет все заглушки о надвигающемся событии, ждет, пока все запрошенные активируются для активации этого типа события, а затем отправляет событие в документ, который может прослушивать любое микроприложение. Это. Учитывая, что все наши iframe имеют одинаковое происхождение, мы можем обратиться от iframe к странице и наоборот, чтобы найти элементы, для которых можно запускать события.

Маршрутизация

В наши дни мы все привыкли ожидать, что строка URL-адреса в SPA будет представлять состояние просмотра приложения, поэтому мы можем вырезать, вставлять, отправлять по почте, текст и ссылаться на него, чтобы перейти непосредственно на страницу в приложении. Однако в приложении с микроинтерфейсом состояние приложения на самом деле представляет собой комбинацию состояний, по одному для каждого микроприложения. Как мы должны представлять и контролировать это?

Решение состоит в том, чтобы закодировать состояние каждого микроприложения в один составной URL-адрес и использовать небольшой маршрутизатор макроприложений, который знает, как объединить этот составной URL-адрес и разобрать его на части. К сожалению, для этого в каждом микроприложении требуется специфичная для Yumcha логика: получать сообщения от маршрутизатора макроприложения и обновлять состояние микроприложения и, наоборот, сообщать маршрутизатору макроприложения об изменениях в этом состоянии, чтобы можно было обновить составной URL-адрес. Например, можно представить себе YumchaLocationStrategy для Angular или элемент <YumchaRouter> для React.

Составной URL-адрес, представляющий состояние макроприложения. Его строка запроса декодируется в две отдельные (двойно закодированные) строки запроса, которые затем передаются микроприложениям, идентификаторы которых указаны в качестве их ключей.

Случай без iframe

Как упоминалось выше, размещение микроприложений в iframe имеет некоторые недостатки. Есть два варианта: включить их непосредственно в HTML-код страницы или поместить в теневой DOM. Обе альтернативы несколько отражают плюсы и минусы iframe , но иногда по-разному.

Например, отдельные политики CSP для микроприложений должны быть каким-то образом объединены. Вспомогательные технологии, такие как программы для чтения с экрана, должны работать лучше, чем iframe , при условии, что они поддерживают теневой DOM (что еще не все поддерживают). Должно быть несложно организовать регистрацию сервис-воркеров микроприложения, используя концепцию «области действия» сервис-воркеров, хотя приложение должно гарантировать, что его сервис-воркер зарегистрирован под именем приложения, а не "/" . Ни одна из проблем макета, связанных с iframe , не применима к встроенным или теневым методам DOM.

Однако приложения, созданные с использованием таких фреймворков, как Angular и React, скорее всего, будут недовольны встроенными или теневыми DOM. Для них мы, вероятно, захотим использовать iframe .

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


В конце концов, реализовать логику встроенных и теневых микроприложений DOM в <yumcha-portal> несложно. Мы извлекаем содержимое данного микроприложения из того места, где оно было вставлено на страницу логикой сервера в виде HTML-элемента <template> , клонируем его, а затем добавляем к тому, что LitElement вызывает renderRoot , который обычно является теневым DOM элемента, но может также должен быть установлен на сам элемент ( this ) для встроенного (не теневого DOM) случая.

Но ждать! Контент, обслуживаемый сервером микроприложений, представляет собой целую HTML-страницу. Мы не можем вставить HTML-страницу для микроприложения с тегами html , head и body в середину страницы для макроприложения, не так ли?

Мы решаем эту проблему, используя особенность тега template , в который заворачивается содержимое микроприложений, полученное с сервера микроприложений. Оказывается, когда современные браузеры сталкиваются с тегом template , хотя они и не «выполняют» его, они анализируют его и при этом удаляют недействительный контент, такой как теги <html> , <head> и <body> . , сохраняя при этом их внутреннее содержание. Таким образом, теги <script> и <link> в <head> , а также содержимое <body> сохраняются. Это именно то, что нам нужно для вставки содержимого микроприложений на нашу страницу.

Архитектура микроинтерфейса: дьявол кроется в деталях

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

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

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

Список используемой литературы

  • Micro Front Ends — Делаем это в стиле Angular — Часть 1
  • Micro Front Ends — Делаем это в стиле Angular — Часть 2
  • Развитие приложения AngularJS с использованием микрофронтендов
  • Микро-интерфейсы
  • Микросервисы пользовательского интерфейса — изменение анти-шаблона (микрофронтенды)
  • Микросервисы пользовательского интерфейса — анти-шаблон?
  • Создание страниц с использованием Micro-Frontends использует подход, похожий на Yumcha, с обратным прокси-сервером и SSI, который я настоятельно рекомендую.
  • Ресурсы по микро-фронтендам
  • Подиум
  • Я не понимаю микро-интерфейсы. Это довольно хороший обзор типов микроинтерфейсных архитектур и вариантов их использования.
  • Бессерверные микроинтерфейсы с использованием Vue.js, AWS Lambda и Hypernova
  • Micro Frontends: отличный исчерпывающий обзор.