EnglishРусский Map

Misago отказывается от React и переходит на htmx

title
Misago отказывается от React и переходит на htmx
type
summary
summary
Зачем Misago сменил React на островной SSR с htmx и как это повлияло на размер JS-бандлов
tags
web, frontend, django, htmx, forum-software
created
2026-07-29
updated
2026-07-29
lang
ru
translation_of
misago-react-to-htmx
source_updated
2026-07-29
translated
2026-09-01
translator
lllm/antigravity/gemini-3.7-flash-medium

rafalp, автор Django-форума Misago, рассказывает на форуме самого проекта об отказе от React.js в кодовой базе. План был опубликован, когда миграция ещё только намечалась на "конец 2024 года"; конкретные результаты внизу дописывались по мере готовности изменений.

Двойной рендеринг

Путь обработки запроса в Misago тогда выглядел так. Django собирает данные для списка тем и рендерит шаблон, содержащий практически готовый HTML, а также JSON-блок с теми же данными, при этом формы отключены, чтобы ничего не нажималось. Затем скачивается JavaScript, считывает встроенный JSON и заменяет большую часть отрендеренного сервером HTML на эквивалентные компоненты React. Кнопки оживают, временные метки меняются.

О последствиях поддержки обеих половин этот пост по сути и написан:

  • Многие страницы существуют дважды: как шаблоны Django и как компоненты React. Любой, кто настраивает HTML под себя, правит шаблоны, видит, как его изменение на секунду мелькает на экране, а затем React перезаписывает всё разметкой, о необходимости менять которую человек даже не подозревал.
  • Любое представление, доступное и пользователям, и гостям, приходится реализовывать дважды: как представление Django с шаблонами и как маршрут React с компонентами, а для его наполнения нужны ещё API и сериализаторы JSON.
  • Сериализация этого JSON для предварительной подготовки состояния React замедляет генерацию ответа.
  • Переводы дублируются в django.po и djangojs.po, а каталог JavaScript увеличивает объём начальной загрузки.
  • Плагинам, которые хотят внедрять или заменять HTML, приходится поставлять одновременно шаблон Django и компонент React, что вынудило бы добавить шаг сборки JavaScript в misago-docker и требовало от авторов плагинов знания обоих стеков.

Два отвергнутых варианта

Двигаться дальше в сторону SPA: оставить представления и шаблоны Django лишь в минимальном виде для поисковых роботов, а весь интерфейс убрать за API и приложение на React. Либо свести Django к API и поставить перед ним JavaScript-фреймворк с серверным рендерингом, например Next.js или Remix.

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

Что здесь делает htmx

htmx позволяет размечать в шаблоне области страницы как динамические острова, которые при взаимодействии заменяются на свежий отрендеренный сервером HTML. Список тем - один из таких (крупных) островов. Переключение категории запрашивает свежий HTML только для этой области, пока остальная страница, включая, например, уже загруженные результаты поиска в панели навигации, остаётся на месте. Изменение на бэкенде минимально: если запрос пришёл от htmx, вернуть остров вместо всей страницы. Никакой сериализации JSON, никакого отдельного JavaScript.

Сам он формулирует это как декларативную версию $.get("url", "#outlet"), которую все писали на jQuery двадцать лет назад, или Rails Turbolinks, и признаётся, что сам с трудом верит, что пишет такое. Это движение в обратную сторону от направления, куда ушли клиентские фреймворки с гранулярной реактивностью (см. signal-push-pull-algorithm про механизмы, от которых htmx отказывается), и оно растёт из того же стремления, что и dark-mode-web-standards: сначала разобраться, на что способны сама платформа и сервер, прежде чем тащить сторонний runtime, делающий всё за них.

Панель администратора намеренно исключена. Она работает на наборе переиспользуемых представлений Django, закрывающих около 90% задач: добавление новой страницы сводится к выбору базового представления, заполнению параметров, объявлению пары форм и написанию простых шаблонов. Автора это полностью устраивает, и переводить админку на SPA он не планирует.

Он также предупреждает о переходном этапе миграции: в некоторых частях Misago не будет ни React, ни htmx, то есть клик по ссылке или отправка формы приведёт к перезагрузке страницы. Он называет это многостраничным приложением и замечает, что звучит это пугающе только на словах. Там, где перезагрузка была бы действительно невыносима (например, лайк посту или голос в опросе), он планирует временные заглушки на AJAX или htmx.

Цифры

Исходное состояние, Misago 0.39:

  • vendor.js - 679 kB, 214 kB gzipped
  • misago.js - 615 kB, 124 kB gzipped
  • django-i18n.js - 102 kB, 25.4 kB gzipped
  • ленивая загрузка: hljs.js 144 kB / 49 kB gzipped, zxcvbn.js 820 kB / 430 kB gzipped

Позже были добавлены два результата. Страницу "Forum options" заменили на "Account settings" (PR #1742), полностью собранную из представлений Django с htmx для динамических обновлений на некоторых страницах: размер misago.js снизился до 578 kB (107 kB gzipped) - экономия 37 kB в исходном виде и 17 kB gzipped. Затем практически с нуля на представлениях Django и htmx были переписаны списки тем (PR #1758): 530 kB (99 kB gzipped), ещё минус 48 kB в исходном виде и 8 kB gzipped.

В сумме это падение с 615 kB до 530 kB (со 124 kB до 99 kB gzipped) - около 14% от бандла приложения в исходном виде и 20% gzipped только за счёт двух самых объёмных страниц продукта. При этом vendor.js, содержащий сам React и являющийся самым крупным файлом для скачивания (214 kB gzipped), не сдвинется с места до тех пор, пока не будет удалён последний компонент. Эту динамику полезно помнить при чтении отчётов о постепенном отказе от фреймворков: пока фреймворк нельзя выкинуть целиком, заявленная экономия остаётся скромной, а основной выигрыш сдвинут на самый конец.