Концепция микро-фронтенда – это микросервисный подход при разработке клиентской части. В настоящее время есть тенденция создавать мощное и функционально насыщенное веб-приложение, расположенное поверх микросервисной архитектуры. Со временем микро-фронтендовая архитектура становится частью приложения, зачастую ее разрабатывает отдельная команда. Эта архитектура растет, ее становится сложно поддерживать. Возникает так называемый «фронтендовый монолит». Для решения этой проблемы была сформулирована концепция микро-фронтендов.
Команда кросс-функциональна и разрабатывает фичи в сквозном исполнении: от пользовательского интерфейса до базы данных. Микро-фронтенд понятнее монолита и не столь громоздкий. Микро-фронтендовая архитектура такого типа призвана разделить все компоненты приложения на категории в зависимости от их бизнес-составляющей, в масштабах всего стека. В таком случае разработчики фронтенда могут рассчитывать на ту же степень гибкости, тестируемости и скорости разработки, какая привычна для команд с бекенда, работающих с микросервисами.
Как устроен микро-фронтенд?
Стратегии, наилучшие практики и рецепты работы с фронтендом позволяют собрать современное веб-приложение, в разработке которого участвует много команд, которые могут использовать разные JavaScript-фреймворки.
![](https://habrastorage.org/getpro/habr/upload_files/80f/f8c/a60/80ff8ca60bf0b3cb9b0e3ed42961ab37.png)
Вот основные концепции, на которых выстроена микро-фронтендовая архитектура
Технологическая независимость
Каждая команда должна выбирать и совершенствовать стек, не координируясь с другими командами. Собственные элементы позволяют скрывать детали реализации, а другим предоставлять нейтральный интерфейс.
Изолированность кода, создаваемого разными командами
Никогда не используйте среду времени исполнения совместно, даже если разные команды работают с одним и тем же фреймворком. Стройте независимое и самодостаточное приложение. Не полагайтесь на разделяемое состояние или глобальные переменные.
Создание командных префиксов
В случаях, когда изоляция пока недостижима изоляция, используйте соглашения об именовании. Создавайте пространства имен для таблиц стилей, локального хранилища данных, событий и куки; так удастся избежать коллизий и пояснить, кто чем владеет.
Нативные возможности браузера предпочтительнее созданных вами API
Для коммуникации не пишите глобальную систему «публикация/подписка», а пользуйтесь событиями браузера. Если будет необходимость создать API для совместной работы нескольких команд, постарайтесь держать его максимально простым.
Обеспечение надежного веб-дизайна
Все фичи должны быть полезными, даже если JavaScript выполнить не удается. Чтобы улучшить воспринимаемую производительность, пользуйтесь универсальным рендерингом и прогрессивными усовершенствованиями.
Каковы наилучшие практики, принятые в микро-фронтенде
Различные практики, при помощи которых реализуется микро-фронтендовая архитектура:
Единый мета-фреймворк для SPA (одностраничных приложений) комбинирует на одной странице сразу несколько приложений без необходимости обновлять страницу; таковы, в частности, React, Vue, Angular, т.д.
Множество одностраничных приложений находятся по разным URL. Для приложений с разделяемой функциональностью нужно использовать компоненты NPM или Bower.
Обособление микро-приложений в Iframes при помощи Windows. API для отправки сообщений и библиотеки нужны для координации. IFrames разделяют API, предоставляемые их родительским окном.
Различные модули должны обмениваться информацией через разделяемую шину событий. Каждый модуль работает с собственным фреймворком, когда обрабатывает входящие и исходящие события.
Библиотеки компонентов, зависящие от стека главного приложения, различные компоненты и разделы приложения разрабатываются как библиотеки, после чего их «требует» главное приложение. Следовательно, главное приложение состоит из разнородных компонентов.
Как взять на вооружение микро-фронтендовую архитектуру?
Вот как перейти на микро-фронтендовую архитектуру и реализовать микросервисное тестирование с применением веб-компонентов:
Интеграция в браузере
Веб-компоненты открывают путь к созданию фрагментов клиентской части, импортированных в веб-приложения. Эти фрагменты можно упаковывать в микросервисы вместе с компонентами серверной части. Создаваемые сервисы полноценно оснащаются как логикой, так и визуальными представлениями, упакованными вместе. При применении такого подхода приложения для клиентской части редуцируются до маршрутизации с принятием решений относительно того, какой набор компонентов будет отображаться, а также до оркестрации событий, происходящих между различными веб-компонентами.
Веб-компоненты
Веб-компоненты обеспечивают создание многоразовых единиц, импортируемых в веб-приложения. Они подобны виджетам, импортируемым на любую веб-страницу. В настоящее время они нативно поддерживаются в браузерах Chrome, Opera и Firefox. Если тот или иной браузер не поддерживает веб-компоненты нативно, совместимость достигается при помощи полифиллов JavaScript. Веб-компоненты состоят из 4 элементов, которые могут использоваться отдельно или вместе -
Собственные элементы
Теневая DOM
Импорты HTML
Шаблоны HTML
Собственные элементы
При помощи собственных элементов (Custom Elements) создаются собственные HTML-теги и элементы. Каждый элемент сопровождается своими CSS-стилями и скриптами. Если нужно создавать собственные теги, применять стили CSS и добавлять поведения, то это делается при помощи скриптов. Единственный требуемый стандарт – ставить дефис во избежание конфликта с новыми HTML-элементами. Например, при создании списка оформляемых заказов, в котором присутствуют как собственные элементы, так и собственные теги, получаются веб-компоненты и обратные вызовы жизненного цикла элементов. Эти обратные вызовы жизненного цикла позволяют определять поведения, специфичные для разработки компонентов. Обратные вызовы жизненного цикла, используемые с собственными элементами, таковы:
CreatedCallback
– определяет поведение, происходящее, когда компонент прошел регистрацию.AttachedCallback
– определяет поведение, происходящее, когда компонент вставлен в DOM.DetachedCallback
– определяет поведение, происходящее, когда компонент отсутствует в DOM.AttributeChangedCallback
- определяет поведение, происходящее, когда добавляется, меняется или удаляется атрибут.
Наилучший пример с собственными элементами:
class CheckoutBasket extends HTMLElement {
constructer (){...} is created
connectedCallback (){...} attached to DOM
attributeChangedCallback (attr , oldVal , newVal) someone changed an attribute
disconnecteCallback () {...} removed from DOM, cleanup events that have been registered
}
Собственные элементы, применяемые по умолчанию - stencil, svelte, SkateJS, AngularElements, hyperHTML, т.д.
Теневая DOM
Теневая DOM от основной DOM – это API, сочетающий HTML, CSS и JavaScript внутри веб-компонента. Когда они находятся внутри компонента, они отделены от основной объектной модели документа. Такое отделение напоминает следующую ситуацию: пользователь занимается созданием сервисов API, а потребитель сервиса API ничего не знает о его внутреннем строении, так как все, что важно для пользователя – это запросы к API. У такого сервиса нет никакого доступа к внешнему миру, за исключением запрашивания API других сервисов. Подобные возможности были представлены и в веб-компонентах. Обращения к их внутреннему поведению извне не происходит, ха исключением случаев, когда это разрешено преднамеренно. Аналогично, теневая DOM никак не затрагивает основную DOM того документа, в котором находятся компоненты. Основной способ связи между веб-компонентами – это срабатывание событий.
HTML-импорты
Для веб-компонентов HTML-импорты являются механизмом упаковки. HTML-импорты сообщают DOM, где находится веб-компонент. В контексте микросервисов в импорте удаленного местоположения сервиса содержится тот компонент, который нужно использовать. HTML-импорты позволяют переиспользовать и включать HTML-документы посредством других HTML-документов. Заранее заданные компоненты, действующие в качестве HTML-импортов, где каждый из них включает свои собственные стили и скрипты, на самом высоком уровне решают, что HTML-импорт представляет в DOM в настоящий момент, а импортированный документ обрабатывает все прочие вещи.
Оболочка – это наиболее внешняя обертка, состоящая из контейнера для компонента и палитры компонентов. В ее состав должны входить контроллеры или представления, позволяющие пользователю оперировать компонентами.
Контейнер – это фактическая корневая точка, куда должен внедряться HTML-код вложенных приложений. Для всех вложенных приложений у него должна быть единая точка входа.
Палитра компонентов позволяет управлять вложенными приложениями, активными в настоящий момент.
HTML-шаблоны
HTML-шаблон – это элемент, содержащий содержимое с клиентской части, которое не отображается при загрузке страницы. Давайте попробуем понять реализацию микро-фронтендовой архитектуры на примере следующего приложения, написанного на React.Js. Хорошо, когда веб-приложения разрабатываются независимо друг от друга, чтобы при изменении некоторого элемента он не блокировал и не повреждал другие. Вот почему в этом примере новое приложение React должно собираться, запускаться и развертываться отдельно, а другие приложения, с которыми ему придется обмениваться информацией – расценивать как сервисы. Так, в нижеприведенном примере создается заголовок для веб-страницы. Здесь используется React.js. Это новомодная вещь. Давайте воспользуемся create-react-app
для быстрой начальной загрузки:
npm install -g create-react-app
create-react-app head
cd head/
npm start
Теперь смотрите, как быстро у нас получится добавить рендеринг на стороне сервера. Позже эта функция поможет нам при поисковой оптимизации и улучшении производительности. Проще прикрепить это в самом начале.
Затем создадим файл под названием server.js в корне проекта; он будет запускать сервер express, а также сделаем серверный рендеринг react:
const path = require('path');
const fs = require('fs');
const express = require('express');
const React = require('react');
const App = require('./transpiled/App.js').default;
const { renderToString } = require('react-dom/server');
const server = express();
server.get('/', (req, res) => {
const htmlPath = path.resolve(__dirname, 'build', 'index.html');
fs.readFile(htmlPath, 'utf8', (err, html) => {
const rootElem = '
const renderedApp = renderToString(React.createElement(App, null)); res.send(html.replace(rootElem, rootElem + renderedApp)); server.use(express.static(‘build’));const port = process.env.PORT || 8080;server.listen(port, () => { console.log(`App listening on port ${port}`); });
Этот скрипт укореняется там, где находится элемент react (App), отображает его в строку и отправляет в HTML, прежде, чем выдать пользователю. Позже React поднимется на базе этого уже отображенного компонента. Но он работает на NodeJS, а Node JS не понимает JSX или других сравнительно новых видов синтаксиса, например, import, поэтому для транспиляции этого кода перед запуском на сервере использовался babel:
npm install --dev babel-cli babel-preset-es2015
Добавляем две задачи в раздел со скриптами в package.json, чтобы это запустить:
"transpile": "NODE_ENV=production babel src --out-dir transpiled --presets es2015,react-app",
"start:prod": "NODE_ENV=production node server.js"
Вот и все. Теперь запускаем заголовок при помощи:
npm run build
npm run transpile
npm run start:prod
Почему микро-фронтенды так важны?
В наше время, когда появляются все новые веб-приложения, клиентская часть все укрупняется, а важность серверной части снижается. Большая часть кода относится к микро-фронтендовой архитектуре. А монолитный подход с большими веб-приложениями не работает. Должен быть инструмент для разбиения такого монолита на сравнительно мелкие модули, действующие независимо друг от друга. Решение для этой проблемы – микросервисное устройство фронтенда. Код клиентской части пишется исключительно на чистом JavaScript и с применением любых javascript-фреймворков, с которых или на которые осуществлялась миграция.
Каковы достоинства микро-фронтендовой архитектуры?
Что ни день - изобретается новая технология для JavaScript, и число этих технологий растет как снежный ком. Иногда это может напрягать, поскольку у каждой JavaScript-технологии есть свои достоинства и недостатки. А при выборе технологии всегда стремишься к максимальной пользе и минимальным рискам. Рекомендуемые практики микро-фронтенда предполагают, что разные технологии должны применяться для разработки разных сервисов. Вот лишь некоторые достоинства такой архитектуры:
Поддержка изоляции кода и стиля. Каждая команда разработчиков может выбрать себе технологию по вкусу. Как разработка, так и развертывание осуществляется очень быстро.
Способствует непрерывному развертыванию
Тестирование сильно упрощается; когда нужно проверить небольшое изменение, не приходится затрагивать все приложение.
Реновация фронтенда – упрощается косметическое обновление
Повышенная надежность и удобство в поддержке
Заключительные замечания
Вы убедитесь, что в микро-фронтендовой архитектуре как ядро кода, так и интеграция устроены невероятно просто. Одна из критически важных проблем – стандартизация принципов UI/UX. Универсальное решение – опираться на стилевое руководство, среди прочего – на Material Design, Bootstrap. Чтобы все работало гладко, нужно наладить коммуникацию в команде, разработать стандарты и правила, минимизировать трения между разными командами. Все эти практики, рекомендуемые в микро-фронтендовой архитектуре, помогают решить важнейшую проблему: масштабируемость. Бывают приложения, которые начинают разбухать, а с таким ростом сопряжены многочисленные проблемы и конфликты. Если разделить код между командами и правильно организовать его «логистику» - придет качество, будут учтены технологические тренды, а мир обогатится новыми быстрыми решениями.