Html over websockets: стоит ли возвращаться к серверному рендеру
Я наткнулся на статью, которая заставила меня задуматься: а так ли нужна вся эта сложность с React, GraphQL и отдельным фронтендом?
Что это за зверь
Автор — Andros Fenollosa — рассказывает про подход, который называется HTML over WebSockets. Идея простая: сервер отправляет готовый HTML прямо в браузер, а не JSON, который потом нужно превращать в интерфейс.
Традиционное одностраничное приложение (SPA) — это конструктор из кусочков: фреймворк для рендера во фронтенде, API, которое отдаёт данные в виде JSON, и две кодовые базы, которые должны друг другу доверять через контракты.
Звучит знакомо, правда? Мы к этому привыкли и считаем нормой.
Но есть другой путь. Вместо JSON сервер сразу шлёт готовый HTML, а клиент только вставляет его в нужное место. Вся логика рендера остаётся на бэкенде, в одном языке, без контрактов и API.
Этот паттерн называют гипермедиа или HTML over the wire.
Три способа доставить HTML
Автор выделяет три варианта в зависимости от транспорта.
- Через HTTP — запрос за запросом, как в htmx или django-unicorn. Просто и понятно, но каждый раз заново открываем соединение.
- Через SSE (Server-Sent Events) — односторонний канал от сервера к клиенту. Постоянное соединение, но только сервер может что-то отправлять. Подходит для уведомлений или ленты новостей.
- Через WebSockets — постоянный двусторонний канал. Клиент и сервер могут слать друг другу сообщения когда угодно. Это Phoenix LiveView, Django LiveView и их родственники.
СПОСОБЫ ПЕРЕДАЧИ HTML ────────────────────── HTTP ──▶ ──▶ ──▶ Запрос-ответ, каждый раз новое соединение SSE ──▶ ──▶ ──▶ Сервер пушит, клиент только слушает WebSocket ◀──▶ ◀──▶ Постоянный канал, оба могут слать Где ▶ — направление данных
Откуда это взялось
В 2019 году на конференции ElixirConf Крис Маккорд, создатель Phoenix, показал технологию LiveView. За 15 минут он собрал клон Twitter, который работал в реальном времени. Без единой строки рендер-жаваскрипта, без React или Vue.
Это был хайп, и с тех пор идея расползлась на другие языки и фреймворки.
Как это работает
На клиенте жаваскрипт всё-таки есть. Но его задача скромная: открыть WebSocket-соединение и вставить полученный HTML в нужный элемент DOM. Всё.
ТРАДИЦИОННЫЙ СПА vs HTML OVER WEBSOCKETS ───────────────────────────────────────── Браузер Сервер Браузер Сервер │──HTTP────────▶│ │──WS открыт──▶│ │ │──Запрос в базу │ │ │ │──Собирает JSON │ │ │◀──JSON────────│ │◀──HTML───────│ │ │ │ │ │──Парсит JSON──│ │──Вставить───▶│ │──Рендерит HTML│ │ │ │ в DOM │ │ │ 6 шагов 2 шага + JSON посереди HTML сразу
В классическом подходе: запрос → база данных → JSON → парсинг → рендер в браузере.
С WebSockets: открыли соединение один раз → запрос → база → готовый HTML → вставили в DOM.
Разница не в том, что WebSocket магически быстрее. Дело в том, что мы убираем лишний шаг — JSON промежуточный. И сервер может сам пушить обновления, когда что-то меняется, без запроса от клиента.
Что хорошего
Автор выделяет несколько преимуществ:
- Один движок рендера — не нужно синхронизировать состояние между серверным API и клиентским фреймворком.
- Не нужен отдельный API — сервер генерирует HTML и отправляет клиенту напрямую.
- Состояние на сервере — в отличие от htmx, где всё намеренно stateless, здесь процесс на сервере помнит, где клиент находится.
- Реальный real-time — клиенты получают изменения мгновенно, без поллинга.
- Broadcast — сервер может отправить изменения всем подключённым клиентам сразу. Чат, дашборд, мультиплеерная игра — всё это получается почти бесплатно.
- Меньше трафика — одно постоянное соединение вместо повторяющихся TCP-handshake и HTTP-заголовков.
- Почти без жаваскрипта — без тяжёлых фреймворков.
- SEO из коробки — первый HTML рендерится на сервере и индексируется. Но осторожно: поисковый робот не увидит обновления, которые пришли позже по WebSocket. Важный контент должен быть в первом ответе.
- Защита от XSS — сервер экранирует HTML до отправки. Даже если кто-то попытается подсунуть
<script>, он приедет как обычный текст.
Подводные камни
Ресурсы сервера — нужно держать WebSocket открытым и хранить информацию о клиенте в памяти. Горизонтальное масштабирование требует шаринга этого состояния. Но если клиентов не тысячи, проблем обычно нет.
Задержка — если между клиентом и сервером большая физическая дистанция, мгновенность уже не ощущается.
Нет офлайна — если соединение оборвётся, сайт перестаёт работать. Нужно продумывать опыт переподключения.
Кривая обучения — WebSocket-сервер это не просто «вставил скрипт на страницу». Нужно понимать паттерн LiveView.
Какие фреймворки есть
| Язык | Фреймворк | Транспорт | Статус |
|---|---|---|---|
| Elixir | Phoenix LiveView | WebSocket | Зрелый |
| Ruby | Hotwire | HTTP + WS | Активный |
| Python/Django | Django LiveView | WebSocket | Активный |
| Python/Django | Reactor | WebSocket | Активный |
| C#/.NET | Blazor Server | WebSocket | .NET 9 |
| PHP/Laravel | Livewire 3 + Reverb | WebSocket | 2024 |
htmx и Datastar работают через HTTP и SSE соответственно — когда двусторонний канал не нужен. Для них не нужно держать состояние клиента на сервере.
Когда это имеет смысл
SSE — дешёвый вариант для уведомлений, ленты новостей или потока токенов от AI. Один раз открыл HTTP-канал, сервер пушит данные, клиент только слушает. Проще масштабировать, нет состояния на сервере.
WebSockets — когда нужно настоящее двустороннее общение: чат, совместное редактирование, игра, dashboard с кучей интерактивных элементов.
Моя оценка
Статья Andros Fenollosa — хороший обзор подхода. Не глубокая, но достаточная, чтобы понять, стоит ли копать дальше.
Что меня зацепило: сама идея не нова, но исполнение в современных фреймворках стало элегантным. Phoenix LiveView в этом смысле пионер, и видно, что концепция уже обкатана.
Практический вывод: если вы делаете что-то с интенсивным real-time и у вас бэкенд на Elixir, Python или Ruby — попробовать стоит. Для простого блога или лендинга это оверхед.
Меня смущает только одно: отладка. Когда рендер на сервере, а состояние там же, привычные DevTools браузера уже не так помогают. Но это, наверное, вопрос привычки.
Главное — не стоит считать это заменой всем SPA. Это инструмент для конкретных задач. Как и любой другой.
Выводы
- HTML over WebSockets убирает JSON-прослойку и переносит рендер на сервер.
- Подход особенно хорош для real-time сценариев: чаты, совместная работа, живые дашборды.
- Это не универсальная замена SPA, а узкоспециализированный инструмент.
- Главные ограничения — состояние на сервере, переподключение и сложность отладки.
Ссылки
- HTML over WebSockets: real-time SPAs with barely any JavaScript — оригинальная статья Andros Fenollosa
- Phoenix LiveView — флагманская реализация на Elixir
- Django LiveView — реализация для Django
Дмитрий Полухин — продуктовый дизайнер. Пишу про разработку, AI и дизайн интерфейсов. Обо мне, контакты и профили.