Микрофронтенды «выросли» из модного веяния web-разработки в технологию, которую активно используют на больших проектах. Микрофронтенд гибче монолита, может быстрее реагировать на непредвиденные ситуации и дорабатываться под запрос. Этот подход позволяет быстро расширять приложение и разрабатывать на разных фреймворках. Тем не менее один из ключевых недостатков технологии — сложность во взаимодействии между frontend-приложениями. 

Меня зовут Игорь, я frontend-разработчик SimbirSoft. Ранее мы с коллегами рассматривали вопрос построения архитектуры микрофронтендов. А в этой статье я разберу основные способы коммуникации между отдельными приложениями, которые можно применять в коммерческой разработке. Материал будет полезен разработчикам и архитекторам frontend-приложений.

iFrame

Для коммуникации между абсолютно не несвязанными приложениями можно использовать iFrame. Кто-то удивится его применению в коммерческом проекте — многие считают эту технологию устаревшей. Но при передаче данных между frontend-приложениями, которые находятся на разных доменах, у iFrame практически нет альтернатив.

Принцип работы довольно прост. Он состоит в использовании одного приложения как хранилища данных и запрашивании/передаче данных из других приложений. У iFrame в этом контексте почти нет ограничений — мы можем передавать данные вне зависимости от используемых фреймворков и доменов. 

Метод PostMessage позволяет безопасно отправлять кросс-доменные запросы.

Отправка данных выглядит примерно так:

const win = document.getElementById("iframe").contentWindow; // объект iframe
win.postMessage("Сообщение отправлено ", "*"); // отправляем сообщение в iframe 

В приложение, которое обрабатывает это сообщение, оно попадает в событие message и прослушивается так:

window.addEventListener("message", listener, false);

Послать ответ в родительское окно можно с помощью window.parent:

window.parent.postMessage("Какое-нибудь произвольное сообщение", "*");

В целом, этих функций достаточно для коммуникации между приложениями и даже построения EventEmmiter через событие message. Но стоит помнить о безопасности — послать запрос в это хранилище может почти любой пользователь. Чтобы этого избежать, проверяйте event.origin у всех, кто отправляет сообщения. 

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

Схема работы iFrame
Схема работы iFrame

Подведем итоги метода коммуникации по iFrame.

Достоинства:

  • позволяет передавать данные между приложениями на разных доменах;

  • поддерживается всеми современными браузерами.

Недостатки:

  • при разработке могут выявиться особенности, которые влияют на безопасность системы;

  • имеет не лучшую репутацию среди разработчиков;

  • замедляет загрузку приложения.

CustomEvent

CustomEvent API позволяет разработчикам не только создавать кастомные события, но и инициировать их на элементах DOM, передавая данные по цепочке. При этом API довольно прост.

Все, что нам нужно – создать событие с помощью конструктора. Например:

var myEvent = new CustomEvent("event ", {
	data: {
		username: "igor"
	}
});

Далее — подписаться на него:

myElement.addEventListener("event", function(e) {
	console.info("Event is: ", e);
})

И вызвать его с помощью метода dispatch:

myElement.dispatchEvent(myEvent);

Этот подход позволяет писать красивый событийно-ориентированный код, который свяжет работу приложения. Но стоит понимать, что работает этот метод только внутри одного window. Простыми словами — не получится передать событие в другую вкладку браузера. На практике этот метод можно использовать, если внутри одной страницы (благодаря новому Module Federation) контентом управляют разные приложения.

Схема работы CustomEvent
Схема работы CustomEvent

Достоинства:

  • красивый и понятный синтаксис;

  • не замедляет скорость загрузки приложения.

Недостатки:

  • имеет очень ограниченную сферу применения, так как работает только в одном окне;

  • частично поддерживается в IE 9-11 (другой синтаксис) и не поддерживается в IE 6-8.

Storage event

Когда обновляются данные в localStorage или sessionStorage, генерируется событие storage. Его можно использовать для оповещения приложения о новых данных. Обработка этого события выглядит примерно так: 

// срабатывает при обновлениях, сделанных в том же хранилище из других документов
window.onstorage = event => { // можно также использовать window.addEventListener("storage", event => {
  if (event.key != "now") return;
  alert(event.key + ":" + event.newValue + " at " + event.url);
};
localStorage.setItem("now", Date.now());

Важно понимать, что при использовании localStorage мы можем оповещать все window, которые имеют доступ к этому хранилищу, в том числе разные вкладки. В случае session storage мы оповещаем только текущую вкладу (схоже с CustomEvent).

Схема работы Storage event
Схема работы Storage event

Достоинства:

  • не замедляет загрузку страницы;

  • может передавать данные как между вкладками, так и внутри одной из них;

  • поддерживается всеми современными браузерами.

Недостатки:

  • не может передавать данные между доменами (исходя из ограничений localStorage);

  • изначально не предназначен для передачи событий.

BroadcastChannel API

Интерфейс BroadcastChannel представляет собой именованный канал, на который можно подписаться из любого контекста просмотра этого источника. Это позволяет настроить коммуникацию между документами — в разных окнах, вкладках, фреймах и так далее. Основное ограничение — в целях безопасности контексты, которые обмениваются данными, должны принадлежать одному источнику (same origin), что подразумевает одинаковый протокол, домен и порт.

Пример передачи данных:

const broadcastChannel = new BroadcastChannel("channel_identifier");
broadcastChannel.postMessage("Example message");

Слушать этот канал можно точно так же, как и iframe — с помощью события message.

Схема работы BroadcastChannel API
Схема работы BroadcastChannel API

Достоинства:

  • не замедляет загрузку страницы;

  • может передавать данные между вкладками;

  • имеет простой и удобный синтаксис.

Недостатки:

  • не может передавать данные между разными доменами;

  • не поддерживается IE 11, Opera Mini и старыми версиями других браузеров (приблизительно, старше 2016).

Итоги

Все описанные выше методы можно использовать в коммерческом приложении для связи между микрофронтендами в зависимости от текущей ситуации:

  • Если нужно максимально просто передать события внутри текущего окна без хранения дополнительного стейта в сторейдже, стоит использовать CustomEvent.

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

  • При кросс-доменной передачи данных у iFrame почти нет конкурентов. Но в других случаях эта технология избыточна.

Спасибо за внимание!

Полезные материалы для frontend-разработчиков мы также публикуем в наших соцсетях – ВК и Telegram.

Комментарии (0)