Микрофронтенды «выросли» из модного веяния 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.
Достоинства:
позволяет передавать данные между приложениями на разных доменах;
поддерживается всеми современными браузерами.
Недостатки:
при разработке могут выявиться особенности, которые влияют на безопасность системы;
имеет не лучшую репутацию среди разработчиков;
замедляет загрузку приложения.
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) контентом управляют разные приложения.
Достоинства:
красивый и понятный синтаксис;
не замедляет скорость загрузки приложения.
Недостатки:
имеет очень ограниченную сферу применения, так как работает только в одном окне;
частично поддерживается в 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).
Достоинства:
не замедляет загрузку страницы;
может передавать данные как между вкладками, так и внутри одной из них;
поддерживается всеми современными браузерами.
Недостатки:
не может передавать данные между доменами (исходя из ограничений localStorage);
изначально не предназначен для передачи событий.
BroadcastChannel API
Интерфейс BroadcastChannel представляет собой именованный канал, на который можно подписаться из любого контекста просмотра этого источника. Это позволяет настроить коммуникацию между документами — в разных окнах, вкладках, фреймах и так далее. Основное ограничение — в целях безопасности контексты, которые обмениваются данными, должны принадлежать одному источнику (same origin), что подразумевает одинаковый протокол, домен и порт.
Пример передачи данных:
const broadcastChannel = new BroadcastChannel("channel_identifier");
broadcastChannel.postMessage("Example message");
Слушать этот канал можно точно так же, как и iframe — с помощью события message.
Достоинства:
не замедляет загрузку страницы;
может передавать данные между вкладками;
имеет простой и удобный синтаксис.
Недостатки:
не может передавать данные между разными доменами;
не поддерживается IE 11, Opera Mini и старыми версиями других браузеров (приблизительно, старше 2016).
Итоги
Все описанные выше методы можно использовать в коммерческом приложении для связи между микрофронтендами в зависимости от текущей ситуации:
Если нужно максимально просто передать события внутри текущего окна без хранения дополнительного стейта в сторейдже, стоит использовать CustomEvent.
Если же стоит задача передать данные между вкладками только одного домена, лучше остановиться на StorageEvent или BroadcastChannel. Выбор стоит только в том случае, если не требуется поддерживать старые версии браузеров. Если ваш фронтенд их поддерживает — избегайте использования BroadcastChannel. В остальных случаях делайте выбор в зависимости от того, как долго вам нужно хранить эти данные. Если их требуется хранить и после закрытия вкладки — используйте Storage, если нет — BroadcastChannel станет более подходящим инструментом.
При кросс-доменной передачи данных у iFrame почти нет конкурентов. Но в других случаях эта технология избыточна.
Спасибо за внимание!
Полезные материалы для frontend-разработчиков мы также публикуем в наших соцсетях – ВК и Telegram.