Skip to content

Продуктовое описание winow-view

Задача

Приложению на OneScript нужен живой интерфейс: список, который обновляется без перезагрузки, форма, которая показывает ошибку рядом с полем, меню, которое раскрывается. Обычный ответ на это - собрать фронтенд на JS-фреймворке, а вместе с ним получить node_modules, шаг сборки, отдельный конвейер и второй язык в проекте.

htmx предлагает другой путь: сервер отдаёт HTML, а htmx подставляет его в нужный элемент страницы. Alpine.js берёт локальное состояние вроде раскрытого меню. От сервера нужно немного: понять, что запрос пришёл от htmx, сформировать фрагмент разметки и выставить пару заголовков. Ровно это и делает библиотека.

Четыре задачи:

  • читать и выставлять заголовки htmx, не разбираясь каждый раз в их регистре и формате;
  • формировать фрагменты и страницы из шаблонов, различая ответ на hx-запрос и на обычный переход;
  • раздавать htmx и Alpine.js из состава пакета, чтобы в проекте не появлялся сборщик;
  • защищать формы от подделки межсайтового запроса без хранилища на сервере.

Кому пригодится

СитуацияЧто даёт библиотека
Список обновляется без перезагрузкиФрагмент на hx-запрос, страница на обычный переход, один обработчик
Форма показывает ошибку рядом с полемПереопределитьЦель и ПереопределитьСпособЗамены
Клиент должен узнать о событии на сервереУстановитьТриггер: htmx вызовет событие, клиент обновит счётчик
Нельзя ставить Node.js на сервер сборкиhtmx и Alpine.js уже в пакете
Формы принимают POST от вошедшего посетителяCsrfGuard без общего хранилища между процессами
Нужны и HTML-страница, и HTML-фрагмент из одного шаблонаМакет с плейсхолдером @Контент

Отличия от альтернатив

SPA на JS-фреймворке. Даёт максимум возможностей и максимум инфраструктуры: сборка, зависимости, отдельный жизненный цикл, дублирование моделей на двух языках. Для внутреннего приложения с формами и списками это цена без выигрыша. Здесь состояние остаётся на сервере, а язык в проекте один.

Ручная работа с htmx. Заголовки читаются в каждом обработчике по-своему, регистр имён приходится угадывать, HX-Trigger с несколькими событиями собирается вручную и ломается на кавычках. Библиотека закрывает это и покрывает тестами.

Шаблонизатор напрямую. JinjOS сам не знает про макеты, про кэш скомпилированных шаблонов и про запрет выхода за каталог шаблонов. Плюс в экосистеме есть коллизия имени класса Шаблон между JinjOS и winow, и она проявляется только при определённом порядке подключения библиотек. Пакет решает её раз и навсегда.

htmx и Alpine.js с CDN. Просто, пока сервер имеет доступ в интернет и пока версия на CDN не изменилась. В составе пакета файлы зафиксированы, доступны в закрытом контуре, а объявленная версия проверяется тестом.

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

Спецификация и совместимость

Библиотека следует справочнику заголовков htmx: реализованы восемь заголовков запроса и десять заголовков ответа. Поведение HX-Trigger соответствует тому, что ожидает htmx: единственное событие без данных передаётся простым именем, несколько событий или событие с данными - объектом JSON.

КомпонентВерсия
htmx2.0.10
Alpine.js3.15.12
ШаблонизаторJinjOS
Веб-серверwinow

Обе лицензии поставляемых библиотек разрешают распространение в составе пакета; 0BSD не требует даже сохранения текста лицензии.

Зависимость от winow не жёсткая: функции принимают и возвращают Соответствие заголовков и строки, поэтому работают в тестах без поднятого сервера. Сам winow нужен только приложению.

Особенности

Синтаксис JinjOS строже, чем кажется. Внутри {% %} операторы завершаются точкой с запятой ({% КонецЦикла; %}), выражение в {{ }} не может содержать }, а блок {% %} - знак процента. Плейсхолдер макета не должен выглядеть как подстановка: {{{контент}}} шаблонизатор попытается вычислить.

Имя класса Шаблон в экосистеме занято дважды - в JinjOS и в winow - и достаётся библиотеке, подключённой первой. В приложении с #Использовать winow выражение Новый Шаблон(...) вернёт класс winow, рассчитанный на внедрение зависимостей контейнером autumn. Поэтому пакет не полагается на глобальное имя: файл шаблонизатора подключается напрямую и регистрируется под своим. Если JinjOS установлен нестандартно, путь к нему задаётся параметром ПутьКJinjOS.

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

Секрет CSRF задаётся извне. Один и тот же у всех рабочих процессов, разный в разных окружениях, в исходный код не попадает. Токен привязан к сессии, поэтому сессии нужны включёнными.

Ограничения

ОграничениеПричина
Только заголовки htmxРасширения htmx вроде hx-sse и hx-ws своих заголовков не добавляют, отдельной поддержки для них нет
Только шаблонизатор JinjOSДругие шаблонизаторы подключаются мимо ViewRenderer
Кэш шаблонов живёт в памяти процессаПри нескольких рабочих процессах каждый компилирует шаблоны сам
Экранирование не разбирает разметкуФункции экранируют значение, а не проверяют готовый HTML: подставлять надёжнее по частям
Экранирования для JS и CSS нетЗначения, попадающие в скрипт или стиль, требуют других правил
Токен CSRF требует сессийПривязка к сессии - основа схемы: без идентификатора токен не выдаётся
Токен нельзя отозвать досрочноСостояния на сервере нет, поэтому досрочный отзыв невозможен: работает только срок жизни

Смотри также