Back

Real-Time UX с расширением htmx SSE

Real-Time UX с расширением htmx SSE

Большинству веб-приложений в какой-то момент требуются живые обновления — значок уведомлений, индикатор прогресса, дашборд, который обновляется без постоянных запросов. Обычное решение — WebSockets или полноценный SPA-фреймворк. Но если вы уже используете htmx, есть более простой путь: расширение htmx SSE, которое связывает server-sent events напрямую с вашим HTML практически без JavaScript.

Ключевые выводы

  • Server-Sent Events (SSE) обеспечивают односторонний поток данных от сервера к клиенту через стандартный HTTP — идеально подходит для уведомлений, дашбордов и живых лент.
  • Расширение htmx SSE — это отдельный пакет (htmx-ext-sse), который подключает SSE-потоки к вашему HTML с помощью декларативных атрибутов, без необходимости писать пользовательский JavaScript.
  • Три основных атрибута — sse-connect, sse-swap и hx-trigger="sse:<event>" — покрывают большинство паттернов real-time UX.
  • SSE проще в развертывании, чем WebSockets, и устраняет лишние запросы при polling, хотя поддерживает только связь от сервера к клиенту.

Что на самом деле представляют собой Server-Sent Events

Server-Sent Events (SSE) — это встроенный в браузер протокол для одностороннего потока данных от сервера к клиенту через стандартное HTTP-соединение. Сервер держит соединение открытым и отправляет текстовые события, когда есть что сообщить. Браузер получает их через API EventSource.

Формат передачи данных — обычный текст:

event: priceUpdate
data: <li>BTC — $62,400</li>

Каждое событие имеет необязательное имя и полезную нагрузку data. Несколько строк data: объединяются. События разделяются пустой строкой.

Поскольку SSE работает через HTTP, он функционирует через прокси и файрволы без специальной настройки. Он нативно поддерживает автоматическое переподключение. Компромисс заключается в направленности: после открытия соединения клиент не может отправлять сообщения обратно. Для уведомлений, дашбордов, прогресса задач и живых лент это вполне приемлемо. Для чата или совместного редактирования лучше подойдут WebSockets.

Установка расширения htmx SSE

Поддержка SSE не встроена в ядро htmx. Она находится в отдельном пакете htmx-ext-sse. Загрузите оба скрипта и активируйте расширение на элементе-контейнере:

<head>
  <script  src="https://cdn.jsdelivr.net/npm/htmx.org@latest/dist/htmx.min.js"></script>  
  <script  src="https://cdn.jsdelivr.net/npm/htmx-ext-sse@latest"></script>
</head>
<body hx-ext="sse">

Для сборок на основе npm установите с помощью npm install htmx-ext-sse и импортируйте как htmx.org, так и htmx-ext-sse в вашем входном файле.

Примечание: Старый атрибут hx-sse из ранних версий htmx устарел. Используйте hx-ext="sse" с выделенным расширением.

Основные атрибуты для потоковых обновлений htmx

Три атрибута покрывают большинство паттернов real-time UX:

АтрибутНазначение
sse-connect="<url>"Открывает соединение EventSource
sse-swap="<event-name>"Заменяет входящий HTML в элементе
hx-trigger="sse:<event-name>"Запускает htmx-запрос при получении события

Живая лента, которая заменяет собственное содержимое при каждой отправке:

<div hx-ext="sse" sse-connect="/feed" sse-swap="message">
  Загрузка…
</div>

Сервер отправляет событие с data: <p>Новый элемент</p>, за которым следует пустая строка, и htmx заменяет содержимое div — JavaScript не требуется.

Обработка нескольких событий и запуск запросов

Один sse-connect может питать несколько дочерних элементов, каждый из которых слушает свое имя события:

<div hx-ext="sse" sse-connect="/stream">
  <div sse-swap="statsUpdate"></div>
  <div sse-swap="alertBanner"></div>
</div>

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

<div hx-ext="sse" sse-connect="/events">
  <div hx-get="/notifications" hx-trigger="sse:newNotification">
    <!-- обновляется при каждом SSE-событии -->
  </div>
</div>

Чтобы корректно закрыть поток, когда сервер сигнализирует о завершении, добавьте sse-close="done" — соединение закроется при получении события с именем done.

Когда SSE превосходит polling или WebSockets

  • в сравнении с polling: SSE устраняет лишние запросы. Сервер отправляет данные только при изменениях.
  • в сравнении с WebSockets: SSE проще развернуть, работает через HTTP/1.1 и HTTP/2, и не требует специальной серверной инфраструктуры. Используйте WebSockets только когда нужна двунаправленная связь.

Практическое замечание: браузеры на HTTP/1.1 ограничивают количество соединений на домен шестью. Если пользователи открывают несколько вкладок, SSE-соединения конкурируют за этот лимит. Использование HTTP/2 в значительной степени избегает этого ограничения за счет мультиплексирования нескольких потоков через одно соединение.

Заключение

Расширение htmx SSE позволяет добавить настоящий real-time UI по принципу HTML-over-the-wire — живые дашборды, индикаторы прогресса, потоки уведомлений — с помощью нескольких HTML-атрибутов и серверной конечной точки, которая умеет держать соединение открытым. Не требуется библиотека управления состоянием, клиентская маршрутизация или конвейер сборки. Если ваш сервер может передавать текст потоком, ваш UI может быть живым.

Часто задаваемые вопросы

Нет. Server-Sent Events строго односторонние, от сервера к клиенту. Если вам нужно отправлять данные обратно, совместите ваш SSE-поток со стандартными htmx-запросами, используя hx-post или hx-put. Для полностью двунаправленной связи, такой как чат в реальном времени, лучше подходят WebSockets.

Встроенный в браузер API EventSource автоматически пытается переподключиться после короткой задержки. Сервер может контролировать интервал повторных попыток, включив поле retry в поток событий. Расширение htmx SSE наследует это поведение переподключения без какой-либо дополнительной настройки с вашей стороны.

Да, и HTTP/2 фактически рекомендуется. При HTTP/1.1 браузеры ограничивают количество одновременных соединений на домен примерно шестью, поэтому несколько вкладок с открытыми SSE-потоками могут исчерпать этот лимит. HTTP/2 мультиплексирует потоки через одно соединение, фактически снимая ограничение.

Отправьте именованное событие, которое соответствует значению атрибута sse-close на вашем элементе-контейнере. Например, если вы установили sse-close равным done, отправка события с именем done заставит расширение корректно закрыть соединение EventSource на стороне клиента.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay