Сильные стороны и преимущества микро-фронтендов
Опубликовано: 2022-03-11Микро-интерфейсная архитектура — это подход к проектированию, при котором интерфейсное приложение разбивается на отдельные полунезависимые «микроприложения», свободно работающие вместе. Концепция микроинтерфейса смутно вдохновлена микросервисами и названа в их честь.
К преимуществам шаблона микро-интерфейса относятся:
- Архитектуры микроинтерфейса могут быть проще, и, следовательно, их легче анализировать и управлять ими.
- Независимые команды разработчиков могут легче сотрудничать в интерфейсном приложении.
- Они могут предоставить средства для миграции из «старого» приложения, запустив параллельно с ним «новое» приложение.
Хотя микрофронтендам в последнее время уделяется много внимания, пока нет ни одной доминирующей реализации, ни четкого «лучшего» фреймворка для микрофронтенда. На самом деле существует множество подходов в зависимости от целей и требований. См. библиографию для некоторых из наиболее известных реализаций.
В этой статье мы пропустим большую часть теории микрофронтендов. Вот что мы не будем освещать:
- «Разделение» приложения на микроприложения
- Проблемы развертывания, в том числе то, как микроинтерфейсы вписываются в модель 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>
, чтобы браузер не выполнял его.
Преимущества этой настройки заключаются, в первую очередь, в том, что при самом первом запросе 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.
Случай без 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: отличный исчерпывающий обзор.