Главная / Статьи / Обновление до htmx 4: Fetch, явное наследование и что ломается

Обновление до htmx 4: Fetch, явное наследование и что ломается

Понять изменения в архитектуре htmx 4 — от ядра, основанного на Fetch, и прямого наследования атрибутов до замены ошибок, морфинга и истории изменений — и спланировать безопасную миграцию.

2717 слов

Htmx основан на простой, намеренно устаревшей концепции: сервер возвращает HTML, браузер вставляет этот HTML на страницу, и формируется функциональное приложение без необходимости копирования всего интерфейса в виде состояния на стороне клиента. Версия 4 сохраняет эту концепцию, одновременно перестраивая подложку под нее с использованием современных примитивов браузеров. В этом руководстве объясняется, что изменилось и почему, какие изменения могут тайно сломать существующее приложение, и как провести обновление в виде структурированной проверки, а не просто повышения версии. Информация соответствует htmx 4 в версии, описанной на момент написания; перед миграцией убедитесь в актуальности деталей, ознакомившись с текущими записями об обновлениях.

Контракт, сохраняемый htmx 4

Полезно вспомнить, что заменяет htmx. Типичное приложение, отрисовываемое на стороне клиента, запрашивает у API данные в формате JSON, хранит их в JavaScript, на основе этих данных отрисовывает компоненты и постоянно синхронизирует локальное состояние с сервером. htmx устраняет большую часть этого промежуточного слоя. Сервер возвращает ту форму представления, которая действительно нужна пользователю, а именно HTML.

Рассмотрим кнопку с атрибутами вроде hx-post, целью в виде #task-list и стратегией добавления элементов путем присоединения. Когда кто-то нажимает на неё, htmx собирает контекст запроса, отправляет его на сервер, парсит возвращаемый маркап и присоединяет этот маркап к списку задач. Валидация, авторизация, сохранение данных и отображение остаются на сервере. Взаимодействие, навигация, фокусировка и сам документ остаются в браузере.

Это гораздо более подробный способ описания функции fetch(), чем краткое название. Атрибуты описывают управление гипермедиа: какие действия доступны, куда они отправляются и как полученный результат должен быть включён на текущей странице. Возвращаемый фрагмент может сам по себе содержать ссылки и формы, указывающие на следующие допустимые действия, точно так же, как и полная страница. Другими словами, HTML остаётся протоколом приложения; htmx лишь координирует процесс обмена данными.

Версия 4 делает этот публичный интерфейс почти скучно стабильным. Она реорганизует внутренний механизм работы, но результатом не становится новая фреймворк-среда, а более строгие правила сотрудничества HTML, HTTP, DOM и сервера, управляющего состоянием.

Ядро, переписанное с использованием Fetch API

В более ранних версиях использовался XMLHttpRequest. Это было оправдано тем, что htmx должен был работать в более старых браузерах, а XHR обеспечивал события прогресса загрузки, от которых зависели некоторые приложения. Однако со временем это решение, направленное на совместимость, превратилось в архитектурный долг. Команда htmx переписала механизм запросов с использованием API Fetch, основанного на обещаниях, опираясь при этом на эксперименты с более мелким проектом под названием Fixi и на технологию потокового HTML.

Предсказуемая схема названий событий

Более чистая структура проявляется в модели событий. Теперь названия событий следуют единому стандартному шаблону htmx:phase:action, который при необходимости может быть дополнен поддействием (htmx:phase:action:sub-action). Поскольку сначала указывается фаза, можно сразу понять, запускается ли обработчик до или после соответствующего шага.

Каждое событие, связанное с запросом, также получает тот же объект контекста. Расширениям и обработчикам больше не нужно собирать разрозненные детали, специфичные для события, чтобы найти исходный элемент, конфигурацию запроса, ответ и информацию о задержанной замене — всё находится в одном месте. Кроме того, запросы получают фазу finally, которая срабатывает независимо от результата: успеха, неудачи или отмены. Именно здесь следует выполнять операции по очистке, такие как скрытие индикаторов загрузки или восстановление доступности кнопок.

Если ваш код использует обработку событий htmx по их имени, каждый обработчик требует пересмотра, поскольку старые имена не соответствуют новой схеме.

Устаревшие обёртки в пользу платформенных решений

Htmx 4 также устраняет вспомогательные функции, которые дублировали API, доступные браузерами в настоящее время:

  • htmx.addClass() заменяется на element.classList.add()
  • htmx.closest() заменяется на element.closest()
  • htmx.remove() заменяется на element.remove()
  • Это полезное упрощение. Небольшая библиотека не должна постоянно использовать удобные API, если платформа уже включила их, причем альтернативы представляют собой стандартные вызовы DOM, которые уже знакомы любому разработчику.

    Наследование атрибутов теперь является опциональным

    Самое значимое изменение в процессе миграции не связано с Fetch. Htmx 4 прекращает автоматическое наследование большинства атрибутов от родительских элементов.

    Ранее атрибут, заданный в контейнере — например, целевом элементе, подтверждающем сообщении или наборе заголовков запроса — автоматически применялся ко всем потомкам, работающим с htmx. В версии 4 для явного указания такого применения необходимо добавить суффикс :inherited к имени атрибута в родительском элементе. Потомок, желающий расширить наследованное значение или селектор вместо того, чтобы перезаписать его, может использовать суффикс :append.

    Этот суффикс не является декоративным элементом. Он сообщает тем, кто читает шаблон, что атрибут родительского элемента намеренно является частью поведения его детей. Это способствует усилению принципа локальности поведения в htmx: чем ближе объявление к элементу, которым оно управляет, тем меньше информации из внешнего контекста необходимо читателю для воссоздания сценария. Общее поведение по-прежнему возможно; его область применения просто указывается в месте объявления.

    Почему это самая рискованная часть обновления

    Это изменение также создает скрытую модель сбоя. Предположим, что заголовок CSRF всегда был задан в обёртке макета. После обновления страница отображается точно так же, как раньше, но запросы от дочерних элементов больше не содержат этого заголовка, и сервер начинает их отклонять. Ничего не кажется неправильным, пока кто-то не отправит форму.

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

    Ответы на ошибки превращаются в заменяемые фрагменты

    В Htmx 2 ответы с кодами состояния 4xx или 5xx по умолчанию не заменялись. Htmx 4 меняет это правило: он заменяет все HTTP-ответы, кроме 204 No Content и 304 Not Modified.

    Когда ответы с разными кодами состояния должны попадать в разные места или использовать разные правила замены, новый атрибут hx-status позволяет настроить это для каждого класса состояния — например, отправлять ошибки валидации в область сообщений внутри элемента, а серверные ошибки — в баннер на уровне страницы.

    Фактические последствия сказываются на сервере. Теперь каждый ответ с ошибкой должен представлять собой допустимый фрагмент для того элемента, который его получит. Полная трассировка стека или простой JSON-корпус ошибки вставляются в DOM без изменений. Коды состояния сохраняют свое HTTP-значение для кэша, логов и клиентов, в то время как HTML-корпус содержит информацию о форматировании и, в идеале, указание на следующее действие, которое может выполнить пользователь, например, исправленную форму.

    Обновление нескольких областей из одного ответа

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

    Htmx 4 добавляет более явный инструмент — элемент <hx-partial>. Каждый частичный элемент указывает собственную цель и стратегию замены, благодаря чему ответ представляет собой список четко определенных обновлений.

    Также определяется порядок отображения. Сначала заменяется основной ответ; за ним следуют частичные элементы и элементы, находящиеся вне основного потока, в порядке их появления в документе. Это способствует тому, чтобы каждое обновление имело смысл само по себе, а не зависело от побочных эффектов предыдущих изменений DOM в том же ответе.

    Это позволяет осуществлять легкую оркестрацию ответов. Сервер может описать все видимые последствия одной операции в одном ответе, не возвращая JSON и не оставляя на клиентском коде задачу распределения полей между компонентами.

    Замены, сохраняющие состояние браузера

    Замена innerHTML кажется простой с точки зрения логики, но при этом уничтожается состояние, находящееся в управлении браузера. Поле ввода может потерять выделение текста, фокус может сместиться, видео может начаться заново, а пользовательский элемент может быть уничтожен и создан заново, даже если большая часть его маркировки остается неизменной.

    Htmx 4 включает режимы обмена innerMorph и outerMorph, основанные на усовершенствованной версии алгоритма Idiomorph. Вместо того чтобы удалять целевую поддерево, этот механизм сравнивает старые и новые узлы и применяет минимальный набор разумных изменений. Узлы, которые совпадают, сохраняют свою идентичность, а вместе с ней — фокус, выделение текста, позицию воспроизведения и внутреннее состояние.

    Морфинг не всегда является лучшим вариантом. Для простого фрагмента полная замена часто безопаснее и проще в отладке. Морфинг оправдывает себя тогда, когда целевой элемент содержит динамические контроллы форм, пользовательские элементы, медиафайлы или вспомогательные компоненты от сторонних разработчиков, идентичность которых имеет значение. Htmx также предоставляет селекторы для пропуска целых узлов или только их дочерних элементов во время морфинга, что является практичным способом защиты элементов с состоянием, таких как встроенные карты или редакторы с содержимым формата rich text.

    Общий принцип стоит соблюдать даже вне рамок htmx: сервер определяет, каким должен быть HTML-код, а браузер сохраняет физические объекты DOM, которые должны остаться неизменными в процессе перехода.

    Стриминг реализуется в расширениях, а не в основном коде

    Fetch обеспечивает htmx гораздо более прочную основу для потоковых ответов, но сама библиотека не налагает единый протокол потоковой передачи на всех. Вместо этого htmx 4 включает отдельные специализированные расширения для Server-Sent Events, WebSockets и многокомпонентных ответов.

    Расширение hx-multipart, предназначенное для многокомпонентных ответов, поддерживает форматы multipart/mixed и multipart/parallel. Каждый компонент может содержать HTML вместе со своими собственными заголовками действий HX-*. Это позволяет серверу сначала отправить временный заменитель, а затем по мере завершения работ постепенно передавать дополнительные части, обрабатывая каждую из них отдельно, причем без необходимости в ручном создании клиентской системы обмена сообщениями.

    Выбор подходящего способа зависит от структуры потока данных:

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

    Прощаяя модель расширений

    Изоляция потоковой передачи от основного ядра позволяет сохранять его компактность, в то время как границы расширений становятся более функциональными. Расширения в htmx 4 регистрируются напрямую и могут взаимодействовать с этапами запроса, ответа и обмена данными. Чтобы активировать такое расширение, достаточно включить соответствующий скрипт; атрибут hx-ext больше не существует. Если требуется четкая граница, настройки позволяют ограничить список допустимых имен расширений на сайте.

    HCON: компактная нотация для опций атрибутов

    По мере того как атрибуты стали включать всё больше опций, htmx потребовалась синтаксис, менее «шумная», чем JSON, встроенный в значение атрибута. Решением стал HCON — схема обозначения конфигурационных объектов htmx. Она поддерживает пары ключ-значение, разделённые пробелами, флаги в виде логических значений, числа, строкы в кавычках и ключи с точками для вложенности.

    JSON по-прежнему принимается, что удобно, когда конфигурация уже генерируется сервером. HCON предназначена для вручную написанного маркипада: достаточно краткого для быстрого сканирования, но при этом достаточно структурированного, чтобы htmx не требовал отдельного специального парсера для каждого атрибута. Та же схема используется в триггерах, модификаторах замены, конфигурации запросов, заголовках, значениях и ответном заголовке HX-Location, поэтому её знание окупается во всех случаях.

    hx-live для сохранения состояния клиента

    Гипермедиа не устраняет всю локальную интерактивность. Перед отправкой любого запроса открывается выпадающий список. Счётчик символов обновляется при каждом нажатии клавиши. Вкладки, элементы для отображения информации и временные выборы обычно находятся в ведении браузера.

    Новое расширение hx-live решает эти проблемы с помощью небольшого слоя скриптинга, ориентированного на DOM. Оно предоставляет инструменты для формирования запросов, направленные селекторы для поиска близлежащих элементов, утилиты DOM, асинхронные функции, типизированный доступ к атрибутам и значениям данных, а также реактивные связи вроде :text, :class и :hidden.

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

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

    Навигация по истории: загрузка данных заново вместо восстановления снимков

    Htmx 2 хранил снимки истории в localStorage. Восстановление такого снимка могло вернуть изменения DOM, сделанные независимыми скриптами, но не восстановить состояние языка JavaScript, которое их создало. Страница казалась интерактивной, но на самом деле представляла собой застывший DOM: элементы отображались, но за ними не было никакой логики.

    Htmx 4 удаляет этот стандартный кэш. При навигации вперед и назад он снова загружает страницу и помещает её результат в тег <body> или в специально отведённый элемент истории. Правильные заголовки кэширования HTTP позволяют сделать такой запрос практически бесплатным, при этом предоставляя скриптам чистый документ для инициализации.

    Приложениям, которым действительно нужны локальные копии страниц, можно использовать расширение hx-history-cache, которое опирается на объект sessionStorage и делает его поведение явным. Эта схема сохраняется на протяжении всей версии: стандартом является использование свежей копии страницы, а локальная реконструкция — это дополнительная функция, доступная по желанию и имеющая своё название.

    Рассматривайте миграцию как аудит поведения

    Самым безопасным способом обновления является не слепая замена пакетов, а тщательный анализ всех мест, где поведение элементов выходит за рамки их маркапа. Большинство страниц остаются без изменений в своем маркапе; основные усилия требуются в области косвенного наследования, обработчиков событий, работы с ответами, истории переходов и расширений.

    Сначала укажите точную версию htmx 4, затем запустите официальный проверщик обновлений, описанный в документации по миграции. Работайте с его выводами в таком порядке, чтобы избежать конфликтов между переименованными атрибутами:

    1. Переименуйте старый атрибут hx-disable, означающий «игнорировать этот поддерево», в hx-ignore.
    2. Только после этого переименуйте hx-disabled-elt в новый атрибут hx-disable. Если выполнять эти два шага в обратном порядке, один атрибут случайно превратится в другой.
  • Добавьте :inherited там, где родитель должен продолжать влиять на потомков, уделяя особое внимание заголовкам, подтверждениям, целям и элементам включения.
  • Обновите названия обработчиков событий и замените устаревшие вспомогательные функции на встроенные API браузера.
  • Протестируйте ответы типа 4xx и 5xx, поскольку теперь они меняются по умолчанию, и убедитесь, что каждый из них возвращает корректный фрагмент.
  • Протестируйте каждый элемент hx-delete, который больше не отправляет данные входящей формы, если только вы этого не запрашиваете с помощью hx-include.
  • Протестируйте навигацию вперед и назад, порядок обработки запросов, таймауты, очереди запросов и все используемые расширения, помня о том, что hx-ext больше не существует.
  • Когда htmx 4 — подходящий инструмент

    Основное изменение — это использование механизма Fetch, но объединяющей идеей является прозрачность. Атрибут доходит до потомков только тогда, когда это прямо указано в его объявлении. По умолчанию пользователю отображается HTML неудачного запроса, а исключения настраиваются отдельно. В ответах, затрагивающих несколько областей, четко указывается, куда идет каждая часть и как происходит ее замена. Навигация вперед и назад загружает новую страницу, если только вы специально не включите кэш снимков. Расширения подключаются через единый общий цикл жизни, а стандартные методы DOM заменяют те, которые раньше использовались htmx для выполнения своих функций.

    Все это снижает количество скрытых механизмов работы, не перенося состояние приложения в клиентскую среду. Именно такой баланс стремится достичь htmx: насыщенное взаимодействие, контроль со стороны сервера и HTML, который по-прежнему описывает возможности страницы.

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

    Основные выводы

    • Модель htmx остается неизменной: элементы представляют собой гипермедийные контроллеры, ответы — HTML, а состояние находится под управлением сервера.
    • Непрямое наследование атрибутов устранено; отсутствие суффикса :inherited является наиболее частой причиной скрытых сбоев, особенно с заголовками CSRF.
    • Теперь ответы на ошибки по умолчанию заменяются друг другом, поэтому тело каждого ответа кодов 4xx и 5xx должно быть допустимым фрагментом для соответствующей цели.
  • <hx-partial> и механизмы трансформации позволяют делать обновления нескольких регионов и замену элементов с сохранением состояния более явными и предсказуемыми.
  • Стриминг, интерактивность на стороне клиента и кэширование истории перенесены в дополнения, включаемые по желанию, что позволяет сохранить компактность основного кода.
  • Запустите проверку обновлений, затем протестируйте реальные пути запросов; проверка выделяет возможные варианты, но только тестирование позволяет убедиться, что приложение по-прежнему работает.
  • Связанные статьи