Aktualizacja na htmx 4: Fetch, wyraźna dziedziczność i to, co się psuje
Zrozum zmiany w architekturze HTMX 4, od rdzenia opartego na Fetch i wyraźnej dziedziczeniu atrybutów po zamiany błędów, modyfikacje i historię zmian, oraz zaplanuj bezpieczną migrację.
Htmx opiera się na prostym, celowo niemodnym założeniu: serwer zwraca HTML, przeglądarka wkleja ten HTML na stronę i powstaje funkcjonalna aplikacja, bez konieczności kopiowania całej interfejsu jako stanu po stronie klienta. Wersja 4 zachowuje to założenie, jednocześnie przebudowując podstawowe mechanizmy wokół nowoczesnych elementów przeglądarek. Ten przewodnik wyjaśnia, co się zmieniło i dlaczego, które zmiany mogą potajemnie uszkodzić istniejącą aplikację oraz jak przeprowadzić aktualizację w formie uporządkowanej audytoryzacji, a nie tylko podniesienia wersji. Szczegóły odnoszą się do htmx 4 zgodnie z dokumentacją z chwili pisania; przed migracją sprawdź konkretne informacje w aktualnych notatkach wydawniczych.
Umowa zachowywana przez htmx 4
Pomaga to przypomnieć sobie, co zastępuje htmx. Typowe aplikacje renderowane po stronie klienta proszą API o dane w formacie JSON, przechowują je w JavaScriptu, renderują na ich podstawie komponenty i ciągle synchronizują lokalny stan z serwerem. htmx eliminuje większość tej warstwy pośredniej. Serwer odpowiada reprezentacją, której faktycznie potrzebuje użytkownik – czyli HTML.
Rozważmy przycisk oznaczony atrybutami typu hx-post, cel taki jak #task-list oraz strategię modyfikacji polegającą na dodawaniu nowych elementów na końcu listy. Gdy ktoś go kliknie, htmx zbiera kontekst żądania, wysyła go do serwera, parsuje otrzymaną markę i dodaje ją do listy zadaniami. Walidacja, autoryzacja, przechowywanie danych i prezentacja pozostają na serwerze. Interakcje, nawigacja, skupienie uwagi oraz sam dokument pozostają w przeglądarce.
To jest coś więcej niż zwięzły sposób na nazywanie fetch(). Atrybuty opisują kontrolę hipermediów: jakie działania są dostępne, dokąd są wysyłane oraz w jaki sposób uzyskana reprezentacja powinna trafić na obecną stronę. Wracający fragment może sam zawierać linki i formularze przedstawiające kolejne dostępne działania, dokładnie tak jak cała strona. Innymi słowy, HTML pozostaje protokołem aplikacyjnym; htmx jedynie koordynuje cały proces.
Wersja 4 utrzymuje tę publiczną warstwę w niemal nudnej stabilności. To, co zostaje przearanżowane, to procesy zachodzące poniżej niej. Rezultatem nie jest nowa ramka front-endowa, lecz bardziej ścisłe zasady dotyczące współpracy HTML, HTTP, DOM oraz serwera zarządzającego stanem.
Jądro zbudowane na bazie Fetch API
Wcześniejsze wersje opierały się na XMLHttpRequest. Było to sensowne, gdy htmx musiał działać w starszych przeglądarkach, a XHR umożliwiał wykrywanie postępów przesyłania danych, od których zależały niektóre aplikacje. Z czasem jednak ta decyzja dotycząca kompatybilności przekształciła się w problem architektoniczny. Zespół htmx przepisał mechanizm wysyłania żądań przy użyciu API Fetch opartego na obietnicach, czerpiąc inspirację z eksperymentów z mniejszym projektem o nazwie Fixi oraz z techniki streamowania HTML.
Przewidywalny schemat nazywania zdarzeń
Czystsza struktura widoczna jest w modelu zdarzeń. Nazwy zdarzeń spełniają teraz jednolity wzór htmx:phase:action, który opcjonalnie może być rozszerzony o poddziałanie (htmx:phase:action:sub-action). Ponieważ najpierw podana jest faza, można od razu stwierdzić, czy słuchacz zostanie uruchomiony przed czy po określonym kroku.
Każde zdarzenie związane z żądaniem otrzymuje również ten sam obiekt kontekstowy. Rozszerzenia i słuchacze nie muszą już zbierać różnych szczegółów specyficznych dla danego zdarzenia, aby znaleźć element źródłowy, konfigurację żądania, odpowiedź oraz bieżącą transakcję; wszystko to znajduje się w jednym miejscu. Żądania otrzymują dodatkowo fazę finally, która jest uruchamiana niezależnie od wyniku: sukcesu, porażki lub anulowania. To idealne miejsce na czynności końcowe, takie jak ukrycie elementów oznaczających przetwarzanie lub ponowne włączenie przycisków.
Jeśli twoja baza kodu słucha zdarzeń htmx według nazw, każdy słuchacz wymaga przejrzenia, ponieważ stare nazwy nie odpowiadają już nowemu schematowi.
Wrapperzy zastąpione przez platformę
Htmx 4 usuwa również funkcje pomocnicze, które duplikowały API dostępne obecnie przez przeglądarki w sposób niezawodny:
htmx.addClass()zostało zastąpione przezelement.classList.add()
htmx.closest() zostało zastąpione przez element.closest()htmx.remove() zostało zastąpione przez element.remove()To jest zdrowa ewolucja. Mała biblioteka nie powinna wiecznie używać udogodnionych interfejsów, gdy platforma już je wchłonęła, a ich zamienniki to standardowe wywołania DOM, które każdy programista i tak zna.
Dziedziczenie atrybutów jest teraz opcjonalne
Najważniejsza zmiana w ramach migracji nie ma nic wspólnego z Fetch. Htmx 4 przestaje domyślnie dziedziczyć większość atrybutów od elementów przodków.
Wcześniej atrybut umieszczony w kontenerze, na przykład celu, komunikacie potwierdzającym lub zestawie nagłówków żądania, automatycznie odnosił się do wszystkich potomnych elementów obsługiwanych przez htmx. W wersji 4 musisz wyraźnie to określić, dodając sufiks :inherited do nazwy atrybutu w elementie nadrzędnym. Potomek, który chce rozszerzyć odziedziczoną wartość lub selektor zamiast je nadpisać, może użyć sufiksu :append.
Sufiks nie służy jedynie dekoracji. Informuje każdego, kto czyta szablon, że atrybut elementu nadrzędnego jest celowo częścią zachowania jego potomków. To jeszcze bardziej przyspiesza realizację zasady lokalności zachowania w htmx: im bliżej deklaracja znajduje się elementu, którym rządzi, tym mniej kontekstu musi odtworzyć czytelnik. Wspólne zachowanie jest nadal możliwe; jego zasięg jest po prostu określony w miejscu deklaracji.
Dlaczego to jest najbardziej ryzykowna część aktualizacji
Zmiana ta powoduje również ukryty tryb awarii. Załóżmy, że nagłówek CSRF zawsze był definiowany w otaczającym elementzie układu. Po aktualizacji strona wyświetla się dokładnie tak samo jak wcześniej, ale żądania od elementów potomnych już nie zawierają tego nagłówka i zaczynają być odrzucane przez serwer. Nic nie wygląda na nieprawidłowe, dopóki ktoś nie wysła formularza.
Htmx oferuje oficjalny sprawdzacz aktualizacji, który skanuje szablony i skrypty pod kątem ukrytego dziedziczenia, przestarzałych nazw zdarzeń, usuniętych atrybutów oraz przestarzałych interfejsów API. Użyj jego raportu jako listy punktowej wyjścia, a nie gwarancji, a następnie przetestuj rzeczywiste ścieżki żądań, szczególnie te chronione nagłówkami, potwierdzeniami lub wspólnymi celami.
Odpowiedzi na błędy stają się wymiennymi fragmentami
W Htmx 2 odpowiedzi o kodach stanu 4xx lub 5xx domyślnie nie były zamieniane. Htmx 4 odwraca tę zasadę: zamienia każdą odpowiedź HTTP z wyjątkiem 204 No Content i 304 Not Modified.
Gdy różne rodziny kodów stanu powinny trafiać w różne miejsca lub stosować inne zasady zamiany, nowe atrybut hx-status umożliwia konfigurację tego dla poszczególnych klas kodów stanu – na przykład wysyłanie błędów walidacji do obszaru wiadomości wewnątrz strony, podczas gdy błędy serwera trafiają do banera na poziomie strony.
Rzeczywiste konsekwencje dotyczą serwera. Każda odpowiedź błędu musi teraz stanowić ważny fragment dla elementu, który ją otrzyma. Pełny zapis ścieżki wywołań lub surowy treść błędu w formacie JSON zostanie wstawiony do DOM-u bez żadnych zmian. Kody stanu zachowują swoje znaczenie HTTP dla cache’ów, logów i klientów, natomiast treść HTML zawiera elementy prezentacyjne oraz, w idealnym przypadku, informacje o dalszych krokach, które może podjąć użytkownik, np. poprawiony formularz.
Zaktualizowanie kilku obszarów z jednej odpowiedzi
Jedna operacja po stronie serwera często wymaga zmiany nie tylko elementu, na który kliknął użytkownik. Wysłanie wiadomości może oznaczać jej dodanie do chronologii, zwiększenie licznika nieprzeczytanych wiadomości oraz zastąpienie elementu sterującego paginacją. Htmx od dawna oferuje rozwiązania typu out-of-band do tego celu: elementy w odpowiedzi oznaczone jako out-of-band zastępują odpowiadające im elementy w innych częściach dokumentu.
Htmx 4 wprowadza bardziej wyraźne narzędzie w postaci elementu <hx-partial>. Każdy taki element określa własny cel oraz strategię wymiany, dzięki czemu odpowiedź jest prezentowana jako lista wyraźnie sformatowanych aktualizacji.
Zdefiniowano również kolejność wyświetlania treści. Najpierw wymieniana jest główna odpowiedź; następnie elementy częściowe i te poza standardowym strumieniem danych, w kolejności występowania w dokumencie. To zachęca do tworzenia takich aktualizacji, które mają sens samodzielnie, zamiast polegać na efektach ubocznych wynikających z wcześniejszych zmian w DOM w tej samej odpowiedzi.
Dzięki temu uzyskujemy lekką orkiestrację odpowiedzi. Serwer może opisać wszystkie widoczne konsekwencje jednej operacji w ramach jednej odpowiedzi, bez konieczności zwracania danych w formacie JSON i pozostawiania kodu klienta do rozdzielania pól między poszczególnymi komponentami.
Zmiany typu „morphing”, które utrzymują stan przeglądarki
Zastępowanie innerHTML jest proste do zrozumienia, ale powoduje utratę stanu przechowywanego przez przeglądarkę. Pole tekstowe może stracić wybrany fragment, uwaga użytkownika może przenieść się gdzie indziej, wideo może zostać ponownie uruchomione, a element niestandardowy może zostać usunięty i odtworzony, nawet jeśli większość jego struktury pozostaje niezmieniona.
Htmx 4 wprowadza tryby wymiany innerMorph i outerMorph, oparte na udoskonalonej wersji algorytmu Idiomorph. Zamiast usuwać docelowy poddrzewo, ten mechanizm porównuje stare i nowe węzły oraz stosuje najmniejszą możliwą liczbę zmian. Węzły, które się pokrywają, zachowują swoją tożsamość, a wraz z nią uwagę użytkownika, wybrany fragment tekstu, pozycję odtwarzania oraz stan wewnętrzny.
Morfowanie nie zawsze jest lepszym rozwiązaniem. W przypadku prostego fragmentu całkowita zamiana jest często bezpieczniejsza i łatwiejsza do debugowania. Morfowanie okazuje się korzystne, gdy docelowy element zawiera aktywne kontrolki formularza, niestandardowe elementy, media lub komponenty od third party, których tożsamość ma znaczenie. Htmx umożliwia również użycie selektorów do pomijania całych węzłów lub tylko ich dzieci podczas procesu morfowania, co stanowi praktyczny sposób na ochronę elementów wymagających utrzymania stanu, takich jak wbudowana mapa czy edytor tekstu z formatowaniem.
Ogólna zasada ta jest przydatna również poza htmx: serwer decyduje, jaki powinien być HTML, a przeglądarka zachowuje fizyczne obiekty DOM, które powinny przetrwać tę transformację.
Strumieniowanie funkcjonuje w rozszerzeniach, a nie w jądrze
Fetch zapewnia htmx o wiele lepszą podstawę dla odpowiedzi strumieniowanych, ale sama platforma nie narzuca jednego protokołu strumieniowania wszystkim. Zamiast tego htmx 4 oferuje oddzielne, specjalistyczne rozszerzenia dla Server-Sent Events, WebSockets oraz odpowiedzi typu multipart.
Rozszerzenie hx-multipart obsługuje formaty multipart/mixed i multipart/parallel. Każda część może zawierać HTML wraz z własnymi nagłówkami typu HX-*. Dzięki temu serwer może najpierw wysłać tymczasowy zamiennik, a następnie strumieniować dodatkowe sekcje w miarę realizacji prac, adresując każdą z nich indywidualnie, bez konieczności budowania ręcznie systemu przekazywania wiadomości po stronie klienta.
Wybór odpowiedniego rozszerzenia zależy od charakteru przepływu danych:
- SSE nadaje się do uporządkowanych, jednokierunkowych aktualizacji wysyłanych z serwera.
- WebSockets są odpowiednie do prawdziwie dwukierunkowej komunikacji.
Wszystkie trzy metody są integrowane z tą samą mechanizmem wymiany danych, więc reszta twojego kodu nie musi wiedzieć, który transport dostarczył dany fragment.
Prostszy model rozszerzeń
Zachowanie strumieniowania poza jądrzem oprogramowania sprawia, że jego rozmiar pozostaje mały, a z kolei granice rozszerzeń stają się bardziej funkcjonalne. Rozszerzenia w htmx 4 rejestrują się bezpośrednio i mogą być powiązane z fazami żądania, odpowiedzi oraz wymiany danych. Aby je aktywować, wystarczy dodać odpowiedni skrypt; atrybut hx-ext już nie istnieje. Jeśli potrzebujesz wyraźnej granicy, konfiguracja pozwala ograniczyć listę dopuszczalnych nazw rozszerzeń na danej stronie.
HCON: zwięzła notacja dla opcji atrybutów
Gdy atrybuty zaczęły zawierać coraz więcej opcji, htmx potrzebował składni mniej chaotycznej niż JSON wplecione w wartość atrybutu. Rozwiązaniem jest HCON – notacja obiektów konfiguracyjnych htmx. Obsługuje pary klucz-wartość oddzielone przecinkami, flagi jako wartości logiczne, liczby, łańcuchy tekstowe w cudzysłowach oraz klucze z kropkami do układania struktury wewnątrz siebie.
Wciąż akceptowany jest JSON, co jest wygodne, gdy serwer już generuje konfigurację. HCON skierowany jest do ręcznie pisanych znaczników: wystarczająco zwięzłych, by można je było szybko przejrzeć, a jednocześnie wystarczająco ustrukturyzowanych, by htmx nie musiał używać osobnego, specjalnie stworzonego parsera dla każdego atrybutu. Ta sama notacja jest używana we wszystkich elementach takich jak wyzwalacze, modyfikatory zamiany, konfiguracja żądania, nagłówki, wartości oraz nagłówek odpowiedzi HX-Location, więc jej opanowanie raz przynosi korzyści we wszystkich przypadkach.
hx-live dla stanu klienta, który pozostaje
Hipermedia nie eliminuje całkowicie lokalnej interaktywności. Przed wysłaniem żadnego żądania otwiera się lista rozwijana. Licznik znaków aktualizuje się przy każdym naciśnięciu klawisza. Karty, elementy ukrywane i tymczasowe wybory to zazwyczaj sprawa przeglądarki.
Nowa rozszerzenie hx-live obejmuje te przypadki za pomocą małej warstwy skryptowania skupionej na DOM-ie. Oferuje narzędzie do wysyłania zapytań, selektory kierunkowe do znajdowania sąsiednich elementów, narzędzia DOM, pomocniki asynchroniczne, typowany dostęp do atrybutów i wartości danych oraz powiązania reaktywne takie jak :text, :class i :hidden.
Jego kluczowa zasada ma charakter filozoficzny: DOM jest magazynem stanu. Wyrażenie reaktywne odczytuje stan z sąsiednich elementów i aktualizuje ich prezentację. Wszystko, co trwałe, pozostaje własnością serwera i dociera na stronę w formie HTML, tak jak wcześniej.
Rozważaj hx-live jako zawór bezpieczeństwa do załatwiania drobnych zadań związanych z interfejsem użytkownika, a nie jako możliwość tworzenia drugiego aplikacji w przeglądarce. Używaj go wtedy, gdy inaczej musiałbyś pisać powtarzalne szablony słuchaczy zdarzeń dla tymczasowych elementów interfejsu. Gdy stan musi przetrwać nawigację, być udostępniany między użytkownikami, kontrolować uprawnienia lub brać udział w transakcjach, powinien znajdować się na serwerze.
Nawigacja w historii odświeża dane zamiast przywracać zrzuty stanu
Htmx 2 przechowywał zrzuty stanu historii w localStorage. Przywrócenie takiego zrzutu mogło przywołać mutacje DOM dokonane przez niepowiązane skrypty, bez przywrócenia stanu środowiska JavaScript, który je stworzył. Strona wyglądała interaktywnie, ale w rzeczywistości była skamieniałym DOM-em: elementy były wyświetlane, ale nic za nimi nie było połączone.
Htmx 4 usuwa ten domyślny cache. Podczas nawigacji wstecznej i do przodu aplikacja ponownie ładuje stronę i umieszcza jej zawartość w elementie <body> lub w wyznaczonym elemencie historii. Odpowiednie nagłówki cache’owania HTTP mogą sprawić, że taka żądanie będzie niemal bezkosztowe, jednocześnie dostarczając skryptom czysty dokument do inicjalizacji.
Aplikacje, które rzeczywiście potrzebują lokalnych kopii zapasowych, mogą zainstalować rozszerzenie hx-history-cache, które wykorzystuje sessionStorage i jasno określa to zachowanie. Ten schemat jest stosowany we wszystkich wersjach: domyślnie używana jest świeża reprezentacja strony, a lokalna rekonstrukcja to opcjonalna funkcja o określonej nazwie.
Traktuj migrację jako audyt zachowań
Najbezpieczniejsza aktualizacja to nie ślepa zamiana pakietów, lecz krótka analiza wszystkich miejsc, gdzie zachowanie przekracza granice zapisu markupu. Większość stron pozostawia swój markup bez zmian; praca koncentruje się na domyślnej dziedziczeniu, słuchaczach zdarzeń, obsłudze odpowiedzi, historii i rozszerzeniach.
Najpierw ustal dokładną wersję htmx 4, a następnie uruchom oficjalny sprawdzacz aktualizacji opisany w dokumentacji migracyjnej. Przejdź przez jego wyniki w takiej kolejności, aby uniknąć kolizji między przemianowanymi atrybutami:
- Przemień stary atrybut
hx-disable, który oznaczał „zignoruj tę poddrzewo”, nahx-ignore. - Dopiero wtedy przemień
hx-disabled-eltna nowy atrybuthx-disable. Wykonanie tych dwóch kroków w odwrotnej kolejności mogłoby przypadkowo zamienić jeden atrybut w drugi.
:inherited w miejscach, gdzie rodzic musi nadal wpływać na potomki, zwracając szczególną uwagę na nagłówki, potwierdzenia, cele i elementy include.4xx i 5xx, ponieważ są teraz wymieniane domyślnie, i upewnij się, że każda z nich zwraca sensowny fragment.hx-delete, które nie wysyła już danych z otaczającej formularza, chyba że poprosisz o to za pomocą hx-include.hx-ext już nie istnieje.Kiedy htmx 4 jest odpowiednim narzędziem
Zmianą w tytule jest Fetch, ale spójnym motywem jest jawność. Atrybut dociera do potomków tylko wtedy, gdy jego deklaracja to przewiduje. HTML nieudanej żądania jest domyślnie pokazywany użytkownikowi, a wyjątki konfiguruje się osobno. Odpowiedzi dotyczące kilku obszarów precyzyjnie określają, dokąd trafia każda część i w jaki sposób jest zamieniana. Nawigacja wsteczna i do przodu ładuje nową stronę, chyba że celowo włączysz cache zrzutów stanu. Rozszerzenia łączą się za pośrednictwem jednego wspólnego cyklu życia, a standardowe metody DOM przejmują funkcje tam, gdzie htmx wcześniej używało własnych narzędzi.
Wszystko to zmniejsza ukryte zachowania, nie przenosząc stanu aplikacji do frameworka klienckiego. To właśnie takie równowagi dąży osiągnąć htmx: bogata interakcja, autorytet serwera oraz HTML, który nadal opisuje, co strona potrafi robić.
To nie sprawi, że każde interfejsy staną się prostsze. Edytor grafiki, środowisko pracy typu offline-first lub aplikacja oparta na głęboko współpracującym modelu lokalnym mogą uzasadniać bardziej złożoną architekturę klienta. Jednak wiele aplikacji biznesowych składa się głównie z elementów nawigacyjnych, formularzy, tabel, mechanizmów walidacji oraz procesów pracy, które serwer już posiada. W takich przypadkach przekazanie gotowej reprezentacji jest często prostsze niż utrzymywanie synchronizacji dwóch mas stanu.
Główne wnioski
- Model htmx pozostaje niezmieniony: elementy to kontrolki hipermediów, odpowiedzi to HTML, a stan należy do serwera.
- Zniknęła domyślna dziedziczenie atrybutów; brak sufiksów
:inheritedjest najczęstszą przyczyną ukrytych problemów, szczególnie w przypadku nagłóweków CSRF. - Odpowiedzi błędowe są teraz domyślnie wymieniane, więc każda treść odpowiedzi typu
4xxi5xxmusi być ważnym fragmentem dla swojego docelowego interfejsu.
<hx-partial> oraz mechanizmy morphing swaps sprawiają, że aktualizacje wielorégionalne i zamiany z zachowaniem stanu stają się jawne i przewidywalne.Literatura pokrewna
- Gdzie jest napisana funkcja decyduje o tym, co widzi: zakres leksykalny w JavaScript — Dowiedz się, jak JavaScript rozwiązuje nazwy zmiennych poprzez środowiska leksykalne, dlaczego miejsce wywołania nie ma znaczenia przy wyszukiwaniu oraz jak to wpływa na obsługiwanie zdarzeń w React.
- JavaScript Currying Demystified: Closures, Partial Application, Reuse — Dowiedz się, jak technika currying przekształca funkcje JavaScript z wieloma argumentami w powtarzalne łańcuchy z jednym argumentem, w jaki sposób różni się od częściowego stosowania funkcji oraz kiedy warto jej unikać.
- Upgrading a React Codebase to TypeScript 6.0: What Breaks First — Jak bardziej rygorystyczne domyślne ustawienia TypeScript 6.0, poprawka dotycząca inferencji metod, importy podpath #/ oraz typy Temporal wpływają na aplikację React, a także lista kontrolna bezpiecznej migracji.