Оновлення до htmx 4: Fetch, явне успадкування та що може зламатися
Зрозумійте зміни в архітектурі htmx 4 — від ядра, заснованого на Fetch, та прямої спадкуваності атрибутів, до механізмів заміни помилок, трансформації та історії змін, та сплануйте безпечну міграцію.
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 давно пропонує рішення для таких випадків: елементи у відповіді, позначені як out-of-band, замінюють відповідні елементи в інших частинах документа.
Htmx 4 додає більш чіткий інструмент — елемент <hx-partial>. Кожен частковий елемент визначає власну мету та стратегію заміни, тож відповідь формується як список чітко позначених оновлень.
Також визначено порядок обробки. Спочатку замінюється основна відповідь; потім — часткові елементи та елементи, що знаходяться поза межами документа, у порядку їхнього розташування. Це спонукає робити кожне оновлення змістовним самостійно, а не залежати від побічних ефектів попередньої зміни DOM у тій самій відповіді.
Це дозволяє здійснювати легку оркестрацію відповідей. Сервер може описати кожну помітну наслідок однієї операції в одній відповіді, не повертаючи JSON та не змушуючи клієнтський код розподіляти поля між компонентами.
Заміни, які підтримують стан браузера
Заміна innerHTML є простою для розуміння, але вона видаляє стан, який зберігає браузер. Поле введення тексту може втратити виділення, фокус може змістися, відео може початися знову, а користувацький елемент може бути знищений та створений заново, навіть якщо більша частина його маркування залишається незмінною.
Htmx 4 постачає режими обміну innerMorph та outerMorph, побудовані на вдосконаленій версії алгоритму Idiomorph. Замість того, щоб видаляти цільову піддеревину, цей механізм порівнює старі та нові вузли та застосовує найменший можливий набір змін. Вузли, які збігаються, зберігають свою ідентичність, а разом з нею — фокус, виділення, позицію відтворення та внутрішній стан.
Морфінг не завжди є кращим варіантом. Для простого фрагмента повна заміна часто є безпечнішою та простішою у виправленні помилок. Морфінг стає корисним, коли ціль містить динамічні елементи форм, власні елементи, медіафайли чи компоненти від сторонніх розробників, ідентичність яких має значення. Htmx також надає селектори для пропуску цілих вузлів або лише їхніх дочірніх елементів під час морфування, що є практичним способом захисту об’єктів із станом, таких як вбудована карта чи редактор багатого тексту.
Загальний принцип варто дотримуватися навіть поза htmx: сервер визначає, яким має бути HTML, а браузер зберігає фізичні об’єкти DOM, які мають залишитися незмінними під час переходу.
Стрімінг існує у розширеннях, а не в основній частині
Fetch надає htmx значно кращу основу для потокових відповідей, але сам фреймворк не нав’язує всім один конкретний протокол потокової передачі. Натомість htmx 4 постачає окремі, спеціалізовані розширення для Server-Sent Events, WebSockets та багаточастинних відповідей.
Розширення для багаточастинних відповідей hx-multipart підтримує формати multipart/mixed та multipart/parallel. Кожна частина може містити HTML разом із власними заголовками дії HX-*. Це дозволяє серверу спочатку надіслати тимчасовий замінник, а потім потоково надсилати додаткові частини у міру виконання обчислень, адресуючи кожну з них окремо, причому без необхідності створювати власний бус повідомлень на стороні клієнта.
Вибір між цими розширеннями залежить від форми потоку даних:
- SSE підходить для впорядкованих, однонапрямових оновлень, які надсилаються з сервера.
- WebSockets підходять для справжньої двонапрямової передачі повідомлень.
Усі три варіанти використовують одну й ту саму систему обміну даними, тому решта вашого маркапу не залежить від того, який метод передав певний фрагмент.
Простіша модель розширень
Збереження стрімінгу поза ядром дозволяє зберегти його компактністю, а межі розширень стають більш функціональними. Розширення в 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, а потім запустіть офіційний перевірник оновлення, описаний у документації з міграції. Обробляйте його результати у такому порядку, щоб уникнути конфліктів між перейменованими атрибутами:
- Перейменуйте старий атрибут
hx-disable, який означав „ігнорувати цю піддеревину“, наhx-ignore. - Лише після цього перейменуйте
hx-disabled-eltна новийhx-disable. Якщо виконувати ці два кроки у зворотному порядку, один атрибут випадково перетвориться на інший.
:inherited там, де батьківський елемент має продовжувати впливати на нащадків, приділяючи особливу увагу заголовкам, підтвердженням, цілям та елементам includes.4xx та 5xx, оскільки вони тепер за замовчуванням міняються місцями, та переконайтеся, що кожна з них повертає зрозумілий фрагмент.hx-delete, який більше не надсилає дані оточуючої форми, якщо ви не попросите про це за допомогою hx-include.hx-ext більше не існує.Коли htmx 4 — правильний інструмент
Основною зміною є використання Fetch, але об’єднуючою ідеєю є чіткість. Атрибут досягає своїх нащадків лише тоді, коли це прямо вказано у його оголошенні. HTML з невдалої запиту за замовчуванням відображається користувачеві, а винятки налаштовуються окремо. У відповідях, які стосуються кількох регіонів, чітко вказується, куди йде кожна частина та як відбувається її заміна. Навігація вперед та назад завантажує нову сторінку, якщо тільки ви спеціально не увімкнете кеш із збереженими копіями. Розширення підключаються через єдиний спільний цикл життя, а стандартні методи DOM беруть на себе функції там, де раніше htmx використовував власні допоміжні засоби.
Разом усе це зменшує приховану поведінку, не перекладаючи стан додатку у клієнтську фреймворк-систему. Саме такий баланс прагне досягти htmx: насичена взаємодія, авторитет сервера та HTML, який все ще описує можливості сторінки.
Це не зробить кожний інтерфейс простішим. Графічний редактор, робоче середовище, орієнтоване на роботу без підключення до мережі, або додаток, створений на основі інтенсивно співпрацюючої локальної моделі, можуть виправдати використання більш складної клієнтської архітектури. Однак багато бізнес-додатків полягають переважно у навігації, формах, таблицях, процедурах перевірки та робочих потоках, якими вже керує сервер. Для таких додатків надсилання готової інформації часто є простішим, ніж підтримувати синхронність двох машин станів.
Ключові висновки
- Модель htmx залишається незмінною: елементи є гіпермедійними контролами, відповіді — HTML, а станом керує сервер.
- Непряма спадкування атрибутів була видалена; відсутність суфіксів
:inheritedє найчастішою причиною прихованих проблем, особливо щодо заголовків CSRF. - Тепер відповіді на помилки за замовчуванням змінюються, тому кожен тіло повідомлень типу
4xxта5xxмає бути коректним фрагментом для своєї мети.
<hx-partial> та механізми заміни дозволяють робити оновлення кількох регіонів та заміну даних з збереженням стану чіткими та передбачуваними.Пов’язані матеріали
- Де написана функція визначає, що вона бачить: лексичний діапазон JavaScript — Дізнайтеся, як JavaScript вирішує питання імені змінної через лексичні середовища, чому місце виклику ніколи не має значення під час пошуку та як це впливає на обробники у React.
- JavaScript Currying Demystified: Closures, Partial Application, Reuse — Дізнайтеся, як техніка курріювання перетворює функції JavaScript з кількома аргументами на повторно використовувані ланцюжки з одним аргументом, у чому її відмінність від часткового застосування та коли краще її уникати.
- Upgrading a React Codebase to TypeScript 6.0: What Breaks First — Як суворіші стандартні налаштування TypeScript 6.0, виправлення інференції методів, імпорти підшляхів #/ та типи Temporal впливають на React-додаток, а також чек-ліст для безпечного оновлення.