Как стать автором
Обновить
49.59
Just AI
Разработка и внедрение технологий разговорного AI

Инкапсуляция UI на примере чат-виджета

Уровень сложностиСредний
Время на прочтение6 мин
Количество просмотров587

Привет, Хабр! Меня зовут Дмитрий Переверза, я Frontend Team Lead в компании Just AI. В рамках платформенного стрима мы занимаемся разработкой и развитием платформы для создания своих чат‑ботов. Cделать хорошего и полезного бота временами бывает сложно, поэтому для помощи разработчикам мы создаем инструменты, которые помогают ускорить разработку и упростить работу с ботами. В этой статье я расскажу, как реализовать изолированный UI, грамотно организовать код на примере виджета чата, и какие проблемы могут возникнуть в процессе разработки.

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

Все начинается с задачи

Сам кейс и эта статья появились благодаря простой задаче — разместить чат-виджет на сайте. Рассмотрим, как все устроено и с какими проблемами мы столкнулись.

Чат-виджет — это небольшой модуль, который загружается на страницу по определенному URL. При загрузке виджет инициализирует себя, используя некоторые переменные из глобального окружения страницы.

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

Решение проблемы изоляции виджета

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

iFrame (Inline Frame, фрейм) — это код HTML, используемый для встраивания интерактивных медиа, сторонних страниц в ваш сайт. iFrame создает плавающий фрейм (отдельное окно html документа), который находится внутри обычного документа — он позволяет загружать в область заданных размеров любые другие независимые документы, видео и интерактивные медиафайлы на вашу страницу.

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

Как это работает?

Схема 1 «Вызова виджета»
Схема 1 «Вызова виджета»
  1. Создание отдельной страницы для виджета

    Вместо того, чтобы внедрять весь код виджета непосредственно в основное приложение, мы создаем отдельную веб-страницу, которая содержит весь необходимый код виджета. На схеме эта страница обозначена как «Our Page for iframe».

  2. 2) Загрузка виджета через iframe

    Основное приложение (Router) не включает код виджета напрямую, а просто отображает iframe, который загружает эту отдельную страницу по URL.

  3. 3) Изоляция глобальных переменных

    Поскольку iframe создает отдельный контекст выполнения, все глобальные переменные, функции и состояния внутри виджета находятся в изоляции от основной страницы.

Это значит, что:

  • Глобальные переменные виджета не будут перезаписывать или конфликтовать с глобальными переменными основной страницы

  • Код виджета не сможет случайно изменить состояние или поведение основного приложения

  • Можно безопасно размещать несколько таких виджетов на одной странице, каждый в своем iframe, без риска конфликтов

Это решает проблему с перезаписью глобальных переменных, но создает другую: теперь мы не можем взаимодействовать с виджетом из главного окна.

Как восстановить обмен данными между iframe и главным окном?

Для этого используем механизм postMessage. Механизм позволяет отправлять сообщения между окнами, даже если они находятся на разных страницах. Это безопасный и контролируемый способ передачи данных между изолированными контекстам: от iframe к главному окну и наоборот.

Рис 1.  Код с postMessage
Рис 1.  Код с postMessage

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

В текущей архитектуре у нас есть две большие области кода:

  1. Код, отвечающий за инициализацию и работу виджета внутри iframe

  2. Код, управляющий этим виджетом и взаимодействующий с ним из главного окна

Обе части тесно связаны: они должны «знать» структуру сообщений, поддерживать актуальные URL, синхронизировать состояния. Это увеличивает сложность поддержки и риск ошибок.

Как теперь организовать код?

Пример проблемы: Вы изменили URL страницы, которую загружает iframe, но забыли обновить этот же URL в роутере главного приложения. В результате виджет не загрузится или будет работать некорректно.

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

Один из вариантов — вынести общие переменные и переиспользовать их в разных частях приложения или положить их в React Context, это позволит централизованно управлять состоянием и настройками. Но такой подход не всегда подходит, так как мы не всегда можем контролировать, где и как будут использоваться эти переменные.

Помимо этого, хотелось бы настраивать всю фичу в одном месте и не прыгать по всему приложению, боясь упустить что-то.

Схема 2 «Как вызывается виджет»
Схема 2 «Как вызывается виджет»

Описываем всю нашу фичу:

Рис. 2. Код
Рис. 2. Код

Добавляем buildIsolatedWidgetComponents под капотом, эта функция представляет из себя фабрику React-компонентов, которые создаются на основе переданных параметров. Такой подход позволяет централизованно описывать логику и структуру изолированного виджета, не размазывая код по всему проекту.

В нашем случае мы передаем URL страницы, которую нужно загрузить в iframe и дополнительный контент, который также нужно добавить.

На выходе мы имеем Compound Components — это паттерн, при котором несколько компонентов объединяются в один объект, чтобы их можно было использовать вместе, и они знали о контексте друг друга.

Это позволяет:

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

  • В одном месте минимизировать ошибки, связанные с рассинхронизацией кода;

  • Упростить интеграцию и повторное использование виджета в разных частях приложения.

Рис. 3. IWC.IsolatedWidget
Рис. 3. IWC.IsolatedWidget

IWC.IsolatedWidget – обертка, которая вставляет iframe в документ и управляет его состоянием (например, открытием и закрытием). Этот компонент (обозначен как "1" на схеме 2) отвечает за создание и отображение изолированного виджета.

Рис. 4. IWC.RouterSwitchWrapper
Рис. 4. IWC.RouterSwitchWrapper

IWC.RouterSwitchWrapper – компонент для перехвата роута приложения. Он показывает нужную страницу внутри iframe, когда пользователь переходит по определенному URL. Этот компонент (обозначен как "2" на схеме 2) работает совместно с Router, определяя, какую страницу из Pages загрузить в iframe при определенном запросе.

Давайте посмотрим на код очень упрощенной версии такой фабрики:

Рис.5. Упрощенное представление
Рис.5. Упрощенное представление

Общие переменные мы теперь храним в замыкании, что позволяет нам получить к ним простой доступ.

Складываем все вышеперечисленное вместе: IWC.RouterSwitchWrapper перехватывает запрос к /llm-assistant-widget, а IWC.IsolatedWidget создает iframe и загружает туда содержимое этой страницы (на схеме «Our Page for iframe»).Таким образом мы получаем полностью изолированный виджет, работающий в своем собственном контексте, без сильной связи с остальным приложением и описанный в одном месте.

Использовать такой подход можно не только с виджетами в iframe, но и с другими сильно связными компонентами, части которых должны должны находиться в разных частях приложения.

А что получилось в итоге?

Использование фабрики компонентов для создания изолированного UI — это эффективный способ:

  • Снизить связность кода: Компоненты меньше зависят друг от друга, что упрощает поддержку и развитие проекта;

  • Повысить безопасность и надежность: Изоляция предотвращает конфликты глобальных переменных и неожиданные побочные эффекты;

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

  • Минимизировать риски ошибок: Любые изменения в одной части кода не затрагивают другие, что снижает вероятность багов.

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

Инкапсуляция UI виджетов не только упрощает разработку и тестирование, но и повышает общую стабильность приложения за счет уменьшения вероятности конфликтов и неожиданных побочных эффектов. А какие подходы используете вы?

Теги:
Хабы:
0
Комментарии0

Публикации

Информация

Сайт
just-ai.com
Дата регистрации
Дата основания
2011
Численность
101–200 человек
Местоположение
Россия