HTMX: Frontend neu denken - React vergessen
Olivia Novak
Dev Intern · Leapcell

Front-End Entwicklung vollständig von JavaScript gefangen gehalten
Es war einmal, da konnte ich mühelos mit der Django-Vorlage Web-UIs erstellen. Für die sich wiederholenden Teile unterschiedlicher Größe auf der Seite verwendete ich Vorlagen oder Fragmente, um sie zu abstrahieren oder zu kapseln. Natürlich scheute ich mich nicht, JavaScript zu schreiben, um der Seite Interaktivität zu verleihen, wenn es nötig war.
Allerdings hat die auf diese Weise erstellte UI offensichtliche Mängel. Jedes Mal, wenn ein Benutzer mit der Seite interagiert, muss das Backend die gesamte HTML-Seite erneut senden. Dies verschwendet nicht nur Bandbreite, sondern macht die Interaktion auch ungeschickt und ungeschmeidig. Um die Interaktionsmöglichkeiten zu verbessern, entstanden einige AJAX-Bibliotheken. Im Laufe der Zeit übernahm JavaScript immer mehr Aufgaben, und sogar die Operation des Renderns von HTML-Vorlagen auf der Serverseite verlagerte sich allmählich auf die Clientseite. Schließlich läuteten reaktive, komponentenbasierte UIs, die durch React repräsentiert werden, einen Entwicklungsfrühling ein.
React brachte viele revolutionäre Konzepte in die Webentwicklung, wie das virtuelle DOM, den unidirektionalen Datenfluss, JSX und das komponentenbasierte Denken. Es verwandelte das Frontend von einem HTML-zentrierten Client vollständig in einen JavaScript-zentrierten Client. Gleichzeitig zog sich auch das Backend von der Frontend-Rendering-Phase zurück und überließ dem Frontend die Vorherrschaft bei der Generierung von HTML, während es sich auf die Bereitstellung von Daten-APIs konzentrierte. Aber als JavaScript anfing, die Frontend-Entwicklung vollständig zu übernehmen und HTML zu einer Nebenrolle wurde, begannen die Dinge ein wenig außer Kontrolle zu geraten. Sogar der Footer-Inhalt, der vollständig aus statischem HTML besteht, muss über JavaScript (JSX) implementiert werden. Die ursprünglich inhaltsreiche HTML-Seite ist extrem einfach geworden, und es bleibt oft nur noch ein Root-Element für die Einbindung im Body übrig.
Während React in der Frontend-Welt populär wurde, traten seine Mängel allmählich zutage. Um diese Probleme zu lösen, entstehen immer wieder neue Ideen, Frameworks und ökologische Werkzeuge. Begeisterte Frontend-Entwickler finden wie beim Ebnen von Wegen durch Berge und beim Bau von Brücken über Flüsse immer wieder Lösungen für die auftretenden Probleme. Als es kein geeignetes State-Management-Tool gab, wurde Redux entwickelt; als das State-Management als zu kompliziert angesehen wurde, wurden Hooks eingeführt; als der JavaScript-Client nicht SEO-freundlich war, wurden Technologien wie SSR eingesetzt. Durch die kontinuierliche Anhäufung verschiedener Lösungen ist die ursprünglich einfache Lösung immer komplexer geworden und hat schließlich ein riesiges und unübersichtliches "Frankenstein" gebildet. Heutzutage habe ich ein Gefühl der Angst vor der Frontend-Entwicklung. Die ursprünglich einfachen Aufgaben sind jetzt voller umständlicher Details. Beim Schreiben von Frontend-Code bin ich oft von der übermäßigen Komplexität überwältigt.
Was noch schlimmer ist, ist, dass aufgrund der vollständigen Übernahme der Frontend-Entwicklung durch JavaScript die Größe von Frontend-Projekten immer weiter zunimmt, was TypeScript auch zur besten Praxis macht. Ich möchte TypeScript nicht herabsetzen. Tatsächlich ist TypeScript eine gut durchdachte Sprache, die viele Probleme löst, die JavaScript in großen Frontend-Projekten hat, sehr gut. Aber brauchen wir wirklich überall "große" Frontend-Projekte? Was ist die eigentliche Ursache dafür, dass Frontend-Projekte so umständlich und aufgebläht sind? Waren die Hauptziele dieser Frameworks nicht ursprünglich, das Frontend reaktiver, einfacher wiederzuverwenden und ausdrucksstärker zu machen? Doch nun zwingt die Komplexität von React und vielen Frontend-Frameworks, die nacheinander entstanden sind, wie Vue, SolidJS, Svelte usw., Frontend-Entwickler, insbesondere "Pseudo-Frontend-Entwickler" wie mich, kleine Projekte in große und einfache Projekte in komplexe zu verwandeln. Um mit komplexen Projekten umzugehen, scheint die Wahl von TypeScript daher unvermeidlich.
HTMX: Rückkehr zum ursprünglichen Anspruch von HTML
Obwohl ich nicht sicher bin, woher der Name HTMX kommt, vermute ich aufgrund seiner Vision, dass er HTML eXtension bedeuten könnte. HTMX glaubt, dass wir uns darauf konzentrieren sollten, HTML zu verbessern und weiterzuentwickeln. Viele der Mängel von HTML können durch eine bessere Semantisierung behoben werden, z. B. durch eine sinnvolle Verwendung von Tag-Attributen, anstatt JavaScript HTML direkt ersetzen zu lassen. Dies ist die direkte Einführung auf htmx.org: "htmx bietet Ihnen Zugriff auf AJAX, CSS-Übergänge, WebSockets und Server Sent Events direkt in HTML, indem Sie Attribute verwenden, sodass Sie moderne Benutzeroberflächen mit der Einfachheit und Leistungsfähigkeit von Hypertext erstellen können".
Die Kernvision und die Funktionen von HTMX umfassen:
- Progressive Verbesserung: HTMX basiert auf dem Prinzip der progressiven Verbesserung, was bedeutet, dass es zunächst sicherstellt, dass die Seite ohne JavaScript ordnungsgemäß funktioniert, und dann nach und nach weitere Funktionen hinzufügt.
- Geringe Invasivität: HTMX ist bestrebt, die geringste Invasivität in bestehenden Code zu haben. Sie müssen nicht die gesamte Anwendung neu schreiben. Sie können HTMX verwenden, indem Sie nur dort einige Attribute hinzufügen, wo sie benötigt werden.
- Reines HTML: Mit HTMX können Sie viele komplexe Frontend-Funktionen implementieren, ohne JavaScript schreiben zu müssen. Dies macht den Code einfacher zu verstehen und zu warten, insbesondere für Entwickler, die mit HTML und serverseitiger Programmierung besser vertraut sind.
- Kompatibilität mit bestehenden Technologien: HTMX kann nahtlos mit Ihren bestehenden Frameworks und Bibliotheken zusammenarbeiten. Egal, ob Sie Flask, Django, Rails oder andere Backend-Frameworks verwenden, HTMX kann problemlos in diese eingebettet werden.
- Klein und schnell: Im Vergleich zu vielen modernen Frontend-Frameworks ist HTMX sehr schlank, was bedeutet, dass es schneller geladen wird und die Benutzererfahrung verbessert.
Aus diesen Funktionen können wir erkennen, dass das Ziel von HTMX darin besteht, die Frontend-Entwicklung zu vereinfachen und es Entwicklern zu ermöglichen, schnell und effizient hochinteraktive und reaktionsschnelle Webseiten zu erstellen, ohne dabei eine große Menge JavaScript oder komplexe Frontend-Frameworks verwenden zu müssen.
Wenn Sie keine komplexen Frontend-Frameworks und eine große Menge JavaScript-Entwicklung benötigen, werden Sie feststellen, dass viele der Probleme, mit denen das Frontend derzeit konfrontiert ist, leicht zu lösen sind. Zum Beispiel ist es nicht notwendig, die gesamte Seite vollständig zu JavaScript-ifizieren, Sie müssen sich keine Sorgen um die SEO-Probleme machen, die durch die JavaScript-ifizierung der Seite verursacht werden, Sie müssen keine komplexen Zustände verwalten und Sie müssen kein TypeScript einführen, um Engineering-Probleme zu lösen.
Vergleich von Beispielcodes
Schauen wir uns zunächst an, wie man die typische Frontend-Funktion: Autocomplete, unter HTMX implementiert.
<input type="text" hx-trigger="keyup delay:500ms, custom-event" hx-get="/search" hx-target="#search-results" hx-swap="innerHTML"> <div id="search-results"></div>
Seien Sie nicht zu überrascht. Dieses kleine Stück Code ist der gesamte Code für die HTMX-Version von Autocomplete. Wir können sehen, dass HTMX dem normalen HTML-Tag mehrere wichtige Attribute hinzufügt:
- hx-trigger: Es wird verwendet, um anzugeben, wann und wie eine htmx-Aktion, wie z. B. ein AJAX-Aufruf, ausgelöst werden soll. Durch dieses Attribut können Entwickler steuern, wie eine Interaktion mit dem Server initiiert werden soll, wenn bestimmte Ereignisse eintreten (z. B. Klick, Eingabe oder Fokus usw.). In diesem Beispiel werden zwei Trigger-Ereignisse festgelegt: a) Auslösen, wenn das Keyup-Ereignis auf der Eingabe auftritt und nach einer Verzögerung von 500 ms; b) Auslösen, wenn ein Ereignis namens custom-event empfangen wird.
- hx-get: Wenn die htmx-Aktion ausgelöst wird, ist dies der auszuführende Aufruf. hx-get steht für eine GET-Anfrage. Ebenso können auch hx-post, hx-put, hx-delete, hx-patch usw. für Serveraufrufe verwendet werden. Durch Attribute wie hx-get dezentralisiert HTMX das Recht, mit dem Server zu interagieren, auf jedes Tag und ändert damit die traditionelle Einschränkung, dass nur <a/> und <form/> mit dem Server interagieren können.
- hx-target: Wenn die Antwort des Servers zurückgegeben wird, gibt sie den Ort an, an dem die Antwort eingefügt wird. hx-target kann ein beliebiger CSS-Ausdruck sein. Hier verweist er auf den Knoten mit der ID search-results. Standardmäßig ist dies der aktuelle Knoten. Wenn Attribute wie hx-get die Möglichkeit bieten, mit dem Server auf der Seite zu interagieren, bietet hx-target die Möglichkeit, dynamisch auf der Seite zu aktualisieren.
- hx-swap: Wenn die Antwort des Servers zurückgegeben wird, gibt sie an, wie der Inhalt ausgetauscht oder ersetzt werden soll. Standardmäßig ist dies innerHTML, d. h. das HTML innerhalb von #search-results wird durch die vom Server zurückgegebenen Daten ersetzt. hx-swap hat andere Verhaltensweisen, wie z. B. outerHTML, beforeend, afterend, und es ist sogar möglich, Animationseffekte für die Art und Weise des Austauschs hinzuzufügen. Details finden Sie in der Dokumentation.
Diese Attribute sind für Anfänger sehr nützlich. Durch die Beherrschung dieser Attribute können die meisten In-Page-Interaktionen behandelt werden. HTMX bietet auch viele andere hx-* Attribute, die hier nicht einzeln vorgestellt werden. Mit diesen Attributen können wir das Verhalten des Suchfelds einfach steuern und die Effekte erzielen, die ursprünglich viel JavaScript erfordert hätten.
Betrachten wir ein komplexeres Beispiel:
Angenommen, die Anwendung zeigt mehrere Notizbücher an, jedes Notizbuch hat mehrere Notizen und jede Notiz hat detaillierte Informationen. Wir zeigen es in einem dreispaltigen Layout an. Wenn der Benutzer in der Spalte ganz links auf Buch1 klickt, werden die Notizen unter Buch1 in paginierter Form in der zweiten Spalte angezeigt, und dann werden die Details der ersten Notiz in der zweiten Spalte in der dritten Spalte angezeigt.
Hier ist der Code, der mit HTMX implementiert wurde:
- Code für die linke Spalte:
<ul> {% for book in books %} <li> <a hx-get="/books/{{book.id}}" hx-target="#note-list">{{book.name}}</a> </li> {% endfor %} </ul>
- Code für die mittlere Spalte:
<div id="note-list"> {% for note in current_book.notes %} <div onclick="htmx.trigger('#note-detail', 'loadNote', {id: '{{note.id}}'})"> <h2>{{note.title}}</h2> <p>{{note.summary}}</p> </div> {% endfor %} </div>
- Code für die rechte Spalte:
<div id="note-detail" _="on loadNote(data) from body htmx.ajax('GET', `/notes/${data.id}`, '#note-detail')" > <h3>{{title}}</h3> <p>{{detail}}</p> </div>
Auf diese Weise wird durch ein paar einfache Vorlagen und ergänzt durch HTMX-Attribute nicht nur das erste Seitenrendering erreicht, sondern die Seite kann auch entsprechend den Klicks des Benutzers aktualisiert werden. Wenn der Benutzer beispielsweise auf Buch2 klickt, wird eine GET-Anfrage ausgelöst, um auf /books/2 zuzugreifen, und die folgende Antwort wird zurückgegeben (d. h. der Inhalt, der durch die Vorlage der mittleren Spalte generiert wurde):
200 OK
HX-Trigger: {"loadNote": {"id": "book2id1"}}
Content-Type: text/html
<div onclick="htmx.trigger('#note-detail', 'loadNote', {id: 'book2id1'})">
<h2>Hello 1</h2>
<p>World 1</p>
</div>
<div onclick="htmx.trigger('#note-detail', 'loadNote', {id: 'book2id2'})">
<h2>Hello 2</h2>
<p>World 2</p>
</div>
...
Dieses Ergebnis wird von HTMX in #note-list gerendert und erreicht so die Aktualisierung der mittleren Spalte. Da das loadNote-Ereignis im HX-Trigger-Header der zurückgegebenen Daten enthalten ist, wird dieses Ereignis von #node-detail erfasst und eine GET-Anfrage an /notes/book2id1 gesendet, und dann wird seine Antwort in der rechten Spalte gerendert.
Für die oben genannten Funktionen wird die Code-Menge bei der Implementierung mit React deutlich erhöht, und es müssen mehr State-Management- und Komponenteninteraktionslogiken behandelt werden. Hier ist ein einfaches React-Implementierungsbeispiel (vereinfachte Version, die nur die Kernlogik zeigt):
import React, { useState, useEffect } from'react'; const BookList = ({ books }) => { const [currentBook, setCurrentBook] = useState(null); const [currentNote, setCurrentNote] = useState(null); useEffect(() => { if (books.length > 0) { setCurrentBook(books[0]); if (books[0].notes.length > 0) { setCurrentNote(books[0].notes[0]); } } }, [books]); const handleBookClick = (book) => { setCurrentBook(book); if (book.notes.length > 0) { setCurrentNote(book.notes[0]); } }; const handleNoteClick = (note) => { setCurrentNote(note); }; return ( <div className="flex"> <div className="w-1/3"> <ul> {books.map((book) => ( <li key={book.id} onClick={() => handleBookClick(book)}> {book.name} </li> ))} </ul> </div> <div className="w-1/3"> {currentBook && ( <div> {currentBook.notes.map((note) => ( <div key={note.id} onClick={() => handleNoteClick(note)}> <h2>{note.title}</h2> <p>{note.summary}</p> </div> ))} </div> )} </div> <div className="w-1/3"> {currentNote && ( <div> <h3>{currentNote.title}</h3> <p>{currentNote.detail}</p> </div> )} </div> </div> ); }; export default BookList;
Es ist ersichtlich, dass zur Implementierung derselben Funktion mit React mehrere Statusvariablen (wie currentBook und currentNote) definiert werden müssen, um den Seitenstatus zu verwalten, und dass mehr Ereignisbehandlungsfunktionen (wie handleBookClick und handleNoteClick) in der Komponente geschrieben werden müssen, um Benutzerinteraktionen zu behandeln. Im Gegensatz dazu ist die Implementierungsmethode von HTMX prägnanter, die Logik ist klarer und sie ist näher an der traditionellen Server-Rendering-Idee.
Fazit
HTMX hat nicht-Frontend-Entwicklern die Tür zur Frontend-Entwicklung wieder geöffnet. Wenn Sie keine Anwendungen mit extrem hoher Interaktivität wie Tabellenkalkulationen oder Google Maps entwickeln, können Sie HTMX im Grunde genommen gut nutzen, um die bestehenden Frontend-Entwicklungsframeworks zu ersetzen und zum Lightweight-Frontend-Entwicklungsmodus zurückzukehren, der sich auf HTML konzentriert. Mit HTMX müssen Sie sich nicht damit herumschlagen, ob Sie den Client als SPA oder als MPA implementieren sollen. Sie können die am besten geeignete Methode für das Routing wählen, Daten auf die natürlichste Weise anzeigen und Benutzer mit den Daten interagieren lassen (sei es Erstellen, Lesen, Aktualisieren, Löschen oder andere Operationen).
Derzeit befindet sich das HTMX-Ökosystem noch in den Kinderschuhen. Ich freue mich sehr darauf, dass die Mainstream-Backend-Frameworks es eingehend unterstützen oder sogar integrieren. Ich glaube, dass nicht-Frontend-Entwickler ihr Selbstvertrauen zurückgewinnen und problemlos vollständige Produkte entwickeln können, die Web-Frontends enthalten, wenn der Wert von HTMX kontinuierlich entdeckt wird.
Leapcell: Das Beste aus Serverlosem Webhosting
Abschließend möchte ich eine Plattform empfehlen, die sich am besten für die Bereitstellung von Webdiensten eignet: Leapcell
🚀 Erstellen Sie mit Ihrer Lieblingssprache
Entwickeln Sie mühelos in JavaScript, Python, Go oder Rust.
🌍 Stellen Sie unbegrenzt Projekte kostenlos bereit
Zahlen Sie nur für das, was Sie verwenden – keine Anfragen, keine Gebühren.
⚡ Pay-as-You-Go, keine versteckten Kosten
Keine Leerlaufgebühren, nur nahtlose Skalierbarkeit.
📖 Entdecken Sie unsere Dokumentation
🔹 Folgen Sie uns auf Twitter: @LeapcellHQ