Misago отказывается от React и переходит на htmx
- title
- Misago отказывается от React и переходит на htmx
- type
- summary
- summary
- Зачем Misago сменил React на островной SSR с htmx и как это повлияло на размер JS-бандлов
- tags
- web, frontend, django, htmx, forum-software
- sources
- misago-react-to-htmx
- 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 gzippedmisago.js- 615 kB, 124 kB gzippeddjango-i18n.js- 102 kB, 25.4 kB gzipped- ленивая загрузка:
hljs.js144 kB / 49 kB gzipped,zxcvbn.js820 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), не сдвинется с места до тех пор, пока не будет удалён последний компонент. Эту динамику полезно помнить при чтении отчётов о постепенном отказе от фреймворков: пока фреймворк нельзя выкинуть целиком, заявленная экономия остаётся скромной, а основной выигрыш сдвинут на самый конец.