Микро-фронтенд. Обзор архитектуры и рекомендуемые практики

Kate

Administrator
Команда форума
Концепция микро-фронтенда – это микросервисный подход при разработке клиентской части. В настоящее время есть тенденция создавать мощное и функционально насыщенное веб-приложение, расположенное поверх микросервисной архитектуры. Со временем микро-фронтендовая архитектура становится частью приложения, зачастую ее разрабатывает отдельная команда. Эта архитектура растет, ее становится сложно поддерживать. Возникает так называемый «фронтендовый монолит». Для решения этой проблемы была сформулирована концепция микро-фронтендов.

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

Как устроен микро-фронтенд?​

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

80ff8ca60bf0b3cb9b0e3ed42961ab37.png

Вот основные концепции, на которых выстроена микро-фронтендовая архитектура

Технологическая независимость​

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

Изолированность кода, создаваемого разными командами​

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

Создание командных префиксов​

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

Нативные возможности браузера предпочтительнее созданных вами API​

Для коммуникации не пишите глобальную систему «публикация/подписка», а пользуйтесь событиями браузера. Если будет необходимость создать API для совместной работы нескольких команд, постарайтесь держать его максимально простым.

Обеспечение надежного веб-дизайна​

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

Каковы наилучшие практики, принятые в микро-фронтенде​

Различные практики, при помощи которых реализуется микро-фронтендовая архитектура:

  1. Единый мета-фреймворк для SPA (одностраничных приложений) комбинирует на одной странице сразу несколько приложений без необходимости обновлять страницу; таковы, в частности, React, Vue, Angular, т.д.
  2. Множество одностраничных приложений находятся по разным URL. Для приложений с разделяемой функциональностью нужно использовать компоненты NPM или Bower.
  3. Обособление микро-приложений в Iframes при помощи Windows. API для отправки сообщений и библиотеки нужны для координации. IFrames разделяют API, предоставляемые их родительским окном.
  4. Различные модули должны обмениваться информацией через разделяемую шину событий. Каждый модуль работает с собственным фреймворком, когда обрабатывает входящие и исходящие события.
Библиотеки компонентов, зависящие от стека главного приложения, различные компоненты и разделы приложения разрабатываются как библиотеки, после чего их «требует» главное приложение. Следовательно, главное приложение состоит из разнородных компонентов.

Как взять на вооружение микро-фронтендовую архитектуру?​

Вот как перейти на микро-фронтендовую архитектуру и реализовать микросервисное тестирование с применением веб-компонентов:

Интеграция в браузере​

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

Веб-компоненты​

Веб-компоненты обеспечивают создание многоразовых единиц, импортируемых в веб-приложения. Они подобны виджетам, импортируемым на любую веб-страницу. В настоящее время они нативно поддерживаются в браузерах Chrome, Opera и Firefox. Если тот или иной браузер не поддерживает веб-компоненты нативно, совместимость достигается при помощи полифиллов JavaScript. Веб-компоненты состоят из 4 элементов, которые могут использоваться отдельно или вместе -

  1. Собственные элементы
  2. Теневая DOM
  3. Импорты HTML
  4. Шаблоны HTML

Собственные элементы​

При помощи собственных элементов (Custom Elements) создаются собственные HTML-теги и элементы. Каждый элемент сопровождается своими CSS-стилями и скриптами. Если нужно создавать собственные теги, применять стили CSS и добавлять поведения, то это делается при помощи скриптов. Единственный требуемый стандарт – ставить дефис во избежание конфликта с новыми HTML-элементами. Например, при создании списка оформляемых заказов, в котором присутствуют как собственные элементы, так и собственные теги, получаются веб-компоненты и обратные вызовы жизненного цикла элементов. Эти обратные вызовы жизненного цикла позволяют определять поведения, специфичные для разработки компонентов. Обратные вызовы жизненного цикла, используемые с собственными элементами, таковы:

  1. CreatedCallback – определяет поведение, происходящее, когда компонент прошел регистрацию.
  2. AttachedCallback – определяет поведение, происходящее, когда компонент вставлен в DOM.
  3. DetachedCallback – определяет поведение, происходящее, когда компонент отсутствует в DOM.
  4. 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. Чтобы все работало гладко, нужно наладить коммуникацию в команде, разработать стандарты и правила, минимизировать трения между разными командами. Все эти практики, рекомендуемые в микро-фронтендовой архитектуре, помогают решить важнейшую проблему: масштабируемость. Бывают приложения, которые начинают разбухать, а с таким ростом сопряжены многочисленные проблемы и конфликты. Если разделить код между командами и правильно организовать его «логистику» - придет качество, будут учтены технологические тренды, а мир обогатится новыми быстрыми решениями.

 
Сверху