Warum JavaScript HTML auffrisst

Avatar of Mike Turley
Mike Turley am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Die Webentwicklung verändert sich ständig. Ein bestimmter Trend ist in letzter Zeit sehr populär geworden und widerspricht grundlegend der gängigen Meinung darüber, wie eine Webseite erstellt werden sollte. Für die einen ist es aufregend, für die anderen frustrierend, und die Gründe dafür sind schwer zu erklären.

Eine Webseite besteht traditionell aus drei separaten Teilen mit getrennten Zuständigkeiten: HTML-Code definiert die Struktur und Bedeutung des Inhalts auf einer Seite, CSS-Code definiert sein Aussehen und JavaScript-Code definiert sein Verhalten. In Teams mit engagierten Designern, HTML/CSS-Entwicklern und JavaScript-Entwicklern passt diese Trennung der Belange gut zu den Jobrollen: Designer bestimmen die visuellen Elemente und Benutzerinteraktionen auf einer Seite, HTML- und CSS-Entwickler reproduzieren diese visuellen Elemente in einem Webbrowser, und JavaScript-Entwickler fügen die Benutzerinteraktion hinzu, um alles miteinander zu verbinden und „zum Laufen zu bringen“. Man kann an einem Teil arbeiten, ohne sich mit allen dreien befassen zu müssen.

In den letzten Jahren haben JavaScript-Entwickler erkannt, dass sie durch die Definition der Seitenstruktur in JavaScript statt in HTML (mithilfe von Frameworks wie React) die Entwicklung und Wartung von Code für Benutzerinteraktionen vereinfachen können, der andernfalls viel komplexer zu erstellen wäre. Wenn man jemandem sagt, dass das von ihm geschriebene HTML zerstückelt und mit JavaScript vermischt werden muss, von dem er nichts versteht, kann er (verständlicherweise) frustriert werden und anfangen zu fragen, was zum Teufel wir davon haben.

Als JavaScript-Entwickler in einem funktionsübergreifenden Team bekomme ich diese Frage gelegentlich gestellt und habe oft Schwierigkeiten, sie zu beantworten. Alle Materialien, die ich zu diesem Thema gefunden habe, sind für ein Publikum geschrieben, das bereits mit JavaScript vertraut ist – was für diejenigen, die sich auf HTML und CSS konzentrieren, nicht besonders nützlich ist. Aber dieses HTML-in-JS-Muster (oder etwas anderes, das die gleichen Vorteile bietet) wird wahrscheinlich noch eine Weile existieren, daher halte ich es für wichtig, dass jeder, der an der Webentwicklung beteiligt ist, es versteht.

Dieser Artikel enthält Codebeispiele für Interessierte, aber mein Ziel ist es, dieses Konzept so zu erklären, dass es auch ohne sie verstanden werden kann.

Hintergrund: HTML, CSS und JavaScript

Um die Zielgruppe dieses Artikels so breit wie möglich zu fassen, möchte ich einen kurzen Überblick über die Arten von Code geben, die an der Erstellung einer Webseite beteiligt sind, und ihre traditionellen Rollen. Wenn Sie damit Erfahrung haben, können Sie diesen Teil überspringen.

HTML ist für Struktur und semantische Bedeutung

HTML (HyperText Markup Language) Code definiert die Struktur und Bedeutung des Inhalts auf einer Seite. Zum Beispiel enthält das HTML dieses Artikels den Text, den Sie gerade lesen, die Tatsache, dass er sich in einem Absatz befindet, und die Tatsache, dass er nach einer Überschrift und vor einem CodePen kommt.

Nehmen wir an, wir wollen eine einfache Einkaufslisten-App erstellen. Wir könnten mit etwas HTML wie diesem beginnen

Wir können diesen Code in einer Datei speichern, ihn in einem Webbrowser öffnen, und der Browser zeigt das gerenderte Ergebnis an. Wie Sie sehen, stellt der HTML-Code in diesem Beispiel einen Abschnitt einer Seite dar, der eine Überschrift „Shopping List (2 items)“, ein Texteingabefeld, eine Schaltfläche „Add Item“ und eine Liste mit zwei Elementen „Eggs“ und „Butter“ enthält. Auf einer traditionellen Website würde ein Benutzer zu einer Adresse in seinem Webbrowser navigieren, dann würde der Browser dieses HTML von einem Server anfordern, laden und anzeigen. Wenn sich bereits Elemente in der Liste befinden, könnte der Server HTML liefern, in dem die Elemente bereits vorhanden sind, wie in diesem Beispiel.

Versuchen Sie, etwas in das Eingabefeld einzugeben und auf die Schaltfläche „Add Item“ zu klicken. Sie werden feststellen, dass nichts passiert. Die Schaltfläche ist nicht mit Code verbunden, der das HTML ändern kann, und das HTML kann sich nicht selbst ändern. Dazu kommen wir gleich.

CSS ist für das Aussehen

CSS (Cascading Style Sheets) Code definiert das Aussehen einer Seite. Zum Beispiel enthält das CSS dieses Artikels die Schriftart, den Abstand und die Farbe des Textes, den Sie lesen.

Vielleicht ist Ihnen aufgefallen, dass unser Einkaufslistenbeispiel sehr schlicht aussieht. Es gibt keine Möglichkeit für HTML, Dinge wie Abstände, Schriftgrößen und Farben festzulegen. Hier kommt CSS (Cascading Style Sheets) ins Spiel. Auf derselben Seite wie das obige HTML könnten wir CSS-Code hinzufügen, um die Dinge ein wenig zu stylen

Wie Sie sehen können, hat dieses CSS die Schriftgrößen und -stärken geändert und dem Abschnitt eine schöne Hintergrundfarbe gegeben (Designer, bitte nicht @ mich; ich weiß, dass das immer noch hässlich ist). Ein Entwickler kann solche Stilregeln schreiben, und sie werden konsistent auf jede HTML-Struktur angewendet: Wenn wir dieser Seite weitere <section>-, <button>- oder <ul>-Elemente hinzufügen, werden dieselben Schriftänderungen angewendet.

Die Schaltfläche tut jedoch immer noch nichts: Hier kommt JavaScript ins Spiel.

JavaScript ist für das Verhalten

JavaScript-Code definiert das Verhalten interaktiver oder dynamischer Elemente auf einer Seite. Zum Beispiel werden die eingebetteten CodePen-Beispiele in diesem Artikel von JavaScript betrieben.

Ohne JavaScript müssten wir, um die Schaltfläche „Add Item“ in unserem Beispiel zum Laufen zu bringen, spezielles HTML verwenden, um Daten an den Server zurückzusenden (<form action="...">, falls es Sie interessiert). Dann würde der Browser die gesamte Seite verwerfen und eine aktualisierte Version der gesamten HTML-Datei neu laden. Wenn diese Einkaufsliste Teil einer größeren Seite wäre, würde alles andere, was der Benutzer gerade tat, verloren gehen. Nach unten gescrollt? Sie sind wieder ganz oben. Sehen Sie sich ein Video an? Es beginnt von vorne. So funktionierten alle Webanwendungen lange Zeit: Jedes Mal, wenn ein Benutzer mit einer Webseite interagierte, war es, als ob er seinen Webbrowser schloss und wieder öffnete. Das ist bei diesem einfachen Beispiel kein großes Problem, aber bei einer großen, komplexen Seite, deren Laden eine Weile dauern kann, ist es weder für den Browser noch für den Server effizient.

Wenn wir möchten, dass sich auf einer Webseite etwas ändert, ohne die gesamte Seite neu zu laden, benötigen wir JavaScript (nicht zu verwechseln mit Java, was eine völlig andere Sprache ist... fangen Sie nicht damit an). Versuchen wir, etwas hinzuzufügen

Wenn wir jetzt Text in das Feld eingeben und auf die Schaltfläche „Add Item“ klicken, wird unser neues Element zur Liste hinzugefügt und die Elementanzahl oben aktualisiert! In einer echten App würden wir auch Code hinzufügen, um das neue Element im Hintergrund an den Server zu senden, damit es beim nächsten Laden der Seite immer noch angezeigt wird.

In diesem einfachen Beispiel ist die Trennung von JavaScript von HTML und CSS sinnvoll. Traditionell wurden auch kompliziertere Interaktionen auf diese Weise hinzugefügt: HTML wird geladen und angezeigt, und JavaScript läuft danach, um Dinge hinzuzufügen und zu ändern. Wenn die Dinge jedoch komplexer werden, müssen wir die Dinge in unserem JavaScript besser verfolgen.

Wenn wir diese Einkaufslisten-App weiterentwickeln würden, würden wir wahrscheinlich als Nächstes Schaltflächen zum Bearbeiten oder Entfernen von Elementen aus der Liste hinzufügen. Nehmen wir an, wir schreiben das JavaScript für eine Schaltfläche, die ein Element entfernt, aber wir vergessen, den Code hinzuzufügen, der die Gesamtanzahl der Elemente oben auf der Seite aktualisiert. Plötzlich haben wir einen Fehler: Nachdem ein Benutzer ein Element entfernt hat, stimmt die Gesamtzahl auf der Seite nicht mit der Liste überein! Sobald wir den Fehler bemerken, beheben wir ihn, indem wir dieselbe Zeile totalText.innerHTML aus unserem „Add Item“-Code zum „Remove Item“-Code hinzufügen. Jetzt haben wir denselben Code an mehr als einer Stelle dupliziert. Nehmen wir später an, wir möchten diesen Code so ändern, dass anstelle von „(2 items)“ oben auf der Seite „Items: 2“ steht. Wir müssen sicherstellen, dass wir es an allen drei Stellen aktualisieren: im HTML, im JavaScript für die Schaltfläche „Add Item“ und im JavaScript für die Schaltfläche „Remove Item“. Wenn wir das nicht tun, haben wir einen weiteren Fehler, bei dem sich dieser Text nach einer Benutzerinteraktion abrupt ändert.

In diesem einfachen Beispiel können wir bereits sehen, wie schnell diese Dinge unübersichtlich werden können. Es gibt Möglichkeiten, unser JavaScript zu organisieren, um diese Art von Problem einfacher zu handhaben, aber wenn die Dinge immer komplexer werden, müssen wir die Dinge ständig umstrukturieren und umschreiben, um Schritt zu halten. Solange HTML und JavaScript getrennt gehalten werden, kann viel Aufwand erforderlich sein, um sicherzustellen, dass alles zwischen ihnen synchron gehalten wird. Das ist einer der Gründe, warum neue JavaScript-Frameworks wie React an Bedeutung gewonnen haben: Sie sind darauf ausgelegt, die Beziehungen zwischen Dingen wie HTML und JavaScript aufzuzeigen. Um zu verstehen, wie das funktioniert, müssen wir zunächst nur ein ganz kleines bisschen Informatik verstehen.

Zwei Arten der Programmierung

Das Schlüsselkonzept, das hier verstanden werden muss, ist die Unterscheidung zwischen zwei gängigen Programmierstilen. (Es gibt natürlich noch andere Programmierstile, aber wir beschäftigen uns hier nur mit zweien davon.) Die meisten Programmiersprachen eignen sich für den einen oder den anderen, und einige können auf beide Arten verwendet werden. Es ist wichtig, beide zu verstehen, um den Hauptvorteil von HTML-in-JS aus der Sicht eines JavaScript-Entwicklers zu verstehen.

  • Imperative Programmierung: Das Wort „imperativ“ impliziert, dass man einen Computer anweist, etwas zu tun. Eine Zeile imperativen Codes ähnelt einem Imperativsatz im Deutschen: Sie gibt dem Computer eine spezifische Anweisung, die er befolgen soll. Bei der imperativen Programmierung müssen wir dem Computer genau sagen, wie er jede Kleinigkeit tun soll, die wir von ihm verlangen. In der Webentwicklung wird dies langsam als „die alte Art“, Dinge zu tun, betrachtet und ist das, was man mit Vanilla JavaScript oder Bibliotheken wie jQuery macht. Das JavaScript in meinem obigen Einkaufslistenbeispiel ist imperativer Code.
    • Imperativ: „Mache X, dann mache Y, dann mache Z“.
    • Beispiel: Wenn der Benutzer dieses Element auswählt, füge die Klasse .selected hinzu; und wenn der Benutzer die Auswahl aufhebt, entferne die Klasse .selected.
  • Deklarative Programmierung: Dies ist eine abstraktere Ebene über der imperativen Programmierung. Anstatt dem Computer Anweisungen zu geben, „deklarieren“ wir stattdessen, was die Ergebnisse sein sollen, nachdem der Computer etwas getan hat. Unsere Tools (z. B. React) finden automatisch das Wie für uns heraus. Diese Tools sind im Inneren mit imperativem Code aufgebaut, dem wir von außen keine Beachtung schenken müssen.
    • Deklarativ: „Das Ergebnis soll XYZ sein. Tue, was immer du tun musst, damit das passiert.“
    • Beispiel: Dieses Element hat die Klasse .selected, wenn der Benutzer es ausgewählt hat.

HTML ist eine deklarative Sprache

Vergessen Sie JavaScript für einen Moment. Hier ist eine wichtige Tatsache: HTML an sich ist eine deklarative Sprache. In einer HTML-Datei können Sie etwas wie folgt deklarieren

<section>
  <h1>Hello</h1>
  <p>My name is Mike.</p>
</section>

Wenn ein Webbrowser dieses HTML liest, findet er diese imperativen Schritte für Sie heraus und führt sie aus

  1. Erstelle ein section-Element
  2. Erstelle ein heading-Element der Stufe 1
  3. Setze den inneren Text des heading-Elements auf „Hello“
  4. Platziere das heading-Element in das section-Element
  5. Erstelle ein paragraph-Element
  6. Setze den inneren Text des paragraph-Elements auf „My name is Mike“
  7. Platziere das paragraph-Element in das section-Element
  8. Platziere das section-Element in das Dokument
  9. Zeige das Dokument auf dem Bildschirm an

Als Webentwickler sind die Details, wie ein Browser diese Dinge tut, irrelevant; alles, was zählt, ist, dass er sie tut. Dies ist ein perfektes Beispiel für den Unterschied zwischen diesen beiden Arten der Programmierung. Kurz gesagt, HTML ist eine deklarative Abstraktion, die um die imperative Anzeigemaschine eines Webbrowsers gewickelt ist. Es kümmert sich um das „Wie“, sodass Sie sich nur um das „Was“ kümmern müssen. Sie können das Leben genießen, indem Sie deklaratives HTML schreiben, weil die netten Leute bei Mozilla oder Google oder Apple den imperativen Code für Sie geschrieben haben, als sie Ihren Webbrowser bauten.

JavaScript ist eine imperative Sprache

Wir haben uns bereits ein einfaches Beispiel für imperatives JavaScript im obigen Einkaufslistenbeispiel angesehen, und ich habe erwähnt, wie die Komplexität der Funktionen einer App Auswirkungen auf den Aufwand für deren Implementierung und das Potenzial für Fehler bei dieser Implementierung hat. Sehen wir uns nun eine etwas komplexere Funktion an und wie sie durch einen deklarativen Ansatz vereinfacht werden kann.

Stellen Sie sich eine Webseite vor, die Folgendes enthält

  • Eine Liste beschrifteter Kontrollkästchen, wobei jede Zeile beim Auswählen in eine andere Farbe wechselt
  • Text am unteren Rand wie „1 of 4 selected“, der aktualisiert werden soll, wenn sich die Kontrollkästchen ändern
  • Eine Schaltfläche „Select All“, die deaktiviert werden soll, wenn bereits alle Kontrollkästchen ausgewählt sind
  • Eine Schaltfläche „Select None“, die deaktiviert werden soll, wenn keine Kontrollkästchen ausgewählt sind

Hier ist eine Implementierung davon in einfachem HTML, CSS und imperativem JavaScript

Es gibt hier nicht viel CSS-Code, weil ich das wunderbare PatternFly Design System verwende, das den größten Teil des CSS für mein Beispiel bereitstellt. Ich habe ihre CSS-Datei in den CodePen-Einstellungen importiert.

All die kleinen Dinge

Um diese Funktion mit imperativem JavaScript zu implementieren, müssen wir dem Browser mehrere detaillierte Anweisungen geben. Dies ist das englischsprachige Äquivalent zum Code in meinem obigen Beispiel

  • In unserem HTML deklarieren wir die anfängliche Struktur der Seite
    • Es gibt vier Zeilenelemente, von denen jedes ein Kontrollkästchen enthält. Das dritte Kästchen ist aktiviert.
    • Es gibt einen zusammenfassenden Text, der „1 of 4 selected“ lautet.
    • Es gibt eine Schaltfläche „Select All“, die aktiviert ist.
    • Es gibt eine Schaltfläche „Select None“, die deaktiviert ist.
  • In unserem JavaScript schreiben wir Anweisungen, was sich ändern soll, wenn jedes dieser Ereignisse eintritt
    • Wenn sich ein Kontrollkästchen von nicht aktiviert auf aktiviert ändert
      • Suchen Sie das Zeilenelement, das das Kontrollkästchen enthält, und fügen Sie die CSS-Klasse .selected hinzu.
      • Suchen Sie alle Kontrollkästchenelemente in der Liste und zählen Sie, wie viele aktiviert und wie viele nicht aktiviert sind.
      • Suchen Sie das zusammenfassende Textelement und aktualisieren Sie es mit der aktivierten Zahl und der Gesamtzahl.
      • Suchen Sie das Schaltflächenelement „Select None“ und aktivieren Sie es, wenn es deaktiviert war.
      • Wenn jetzt alle Kontrollkästchen aktiviert sind, suchen Sie das Schaltflächenelement „Select All“ und deaktivieren Sie es.
    • Wenn sich ein Kontrollkästchen von aktiviert auf nicht aktiviert ändert
      • Suchen Sie das Zeilenelement, das das Kontrollkästchen enthält, und entfernen Sie die Klasse .selected.
      • Suchen Sie alle Kontrollkästchenelemente in der Liste und zählen Sie, wie viele aktiviert und nicht aktiviert sind.
      • Suchen Sie das zusammenfassende Textelement und aktualisieren Sie es mit der aktivierten Zahl und der Gesamtzahl.
      • Suchen Sie das Schaltflächenelement „Select All“ und aktivieren Sie es, wenn es deaktiviert war.
      • Wenn jetzt alle Kontrollkästchen nicht aktiviert sind, suchen Sie das Schaltflächenelement „Select None“ und deaktivieren Sie es.
    • Wenn auf die Schaltfläche „Select All“ geklickt wird
      • Suchen Sie alle Kontrollkästchenelemente in der Liste und aktivieren Sie sie alle.
      • Suchen Sie alle Zeilenelemente in der Liste und fügen Sie die Klasse .selected hinzu.
      • Suchen Sie das zusammenfassende Textelement und aktualisieren Sie es.
      • Suchen Sie die Schaltfläche „Select All“ und deaktivieren Sie sie.
      • Suchen Sie die Schaltfläche „Select None“ und aktivieren Sie sie.
    • Wenn auf die Schaltfläche „Select None“ geklickt wird
      • Suchen Sie alle Kontrollkästchenelemente in der Liste und deaktivieren Sie sie alle.
      • Suchen Sie alle Zeilenelemente in der Liste und entfernen Sie die Klasse .selected.
      • Suchen Sie das zusammenfassende Textelement und aktualisieren Sie es.
      • Suchen Sie die Schaltfläche „Select All“ und aktivieren Sie sie.
      • Suchen Sie die Schaltfläche „Select None“ und deaktivieren Sie sie.

Wow. Das ist viel, oder? Nun, wir sollten daran denken, für jede einzelne dieser Anweisungen Code zu schreiben. Wenn wir eine dieser Anweisungen vergessen oder vermasseln, bekommen wir einen Fehler, bei dem die Gesamtzahlen nicht mit den Kontrollkästchen übereinstimmen, oder eine Schaltfläche aktiviert ist, die beim Anklicken nichts tut, oder eine Zeile die falsche Farbe hat, oder etwas anderes, woran wir nicht gedacht haben und erst herausfinden, wenn sich ein Benutzer beschwert.

Das große Problem hierbei ist, dass es keine einzige Quelle der Wahrheit für den Zustand unserer App gibt, der in diesem Fall lautet: „Welche Kontrollkästchen sind aktiviert?“ Die Kontrollkästchen wissen natürlich, ob sie aktiviert sind oder nicht, aber die Zeilenstile müssen es auch wissen, der zusammenfassende Text muss es wissen, und jede Schaltfläche muss es wissen. Fünf Kopien dieser Informationen werden separat im gesamten HTML gespeichert, und wenn sich die Informationen an einer dieser Stellen ändern, muss der JavaScript-Entwickler dies erkennen und imperativen Code schreiben, um die anderen synchron zu halten.

Dies ist immer noch nur ein einfaches Beispiel für eine kleine Komponente einer Seite. Wenn sich das wie Kopfschmerzen anhört, stellen Sie sich vor, wie komplex und fragil eine Anwendung wird, wenn Sie das Ganze auf diese Weise schreiben müssen. Für viele komplexe moderne Webanwendungen ist dies keine skalierbare Lösung.

Hin zu einer einzigen Quelle der Wahrheit

Tools wie React ermöglichen es uns, JavaScript auf deklarative Weise zu verwenden. So wie HTML eine deklarative Abstraktion ist, die um die Anzeigebefehle des Webbrowsers gewickelt ist, ist React eine deklarative Abstraktion, die um JavaScript gewickelt ist.

Erinnern Sie sich, wie HTML es uns ermöglichte, uns auf die Struktur einer Seite zu konzentrieren und nicht auf die Details, wie der Browser diese Struktur anzeigt? Nun, wenn wir React verwenden, können wir uns wieder auf die Struktur konzentrieren, indem wir sie basierend auf Daten definieren, die an einem einzigen Ort gespeichert sind. Wenn sich diese Quelle der Wahrheit ändert, aktualisiert React die Struktur der Seite automatisch für uns. Es kümmert sich um die imperativen Schritte hinter den Kulissen, genau wie der Webbrowser es für HTML tut. (Obwohl React hier als Beispiel verwendet wird, ist dieses Konzept nicht einzigartig für React und wird auch von anderen Frameworks wie Vue verwendet.)

Kehren wir zu unserer Liste von Kontrollkästchen aus dem obigen Beispiel zurück. In diesem Fall ist die Wahrheit, die uns interessiert, einfach: Welche Kontrollkästchen sind aktiviert? Die anderen Details auf der Seite (z. B. was die Zusammenfassung besagt, die Farbe der Zeilen, ob die Schaltflächen aktiviert sind oder nicht) sind Effekte, die von derselben Wahrheit abgeleitet werden. Warum sollten sie also ihre eigene Kopie dieser Informationen benötigen? Sie sollten einfach die einzige Quelle der Wahrheit als Referenz verwenden, und alles auf der Seite sollte „einfach wissen“, welche Kontrollkästchen aktiviert sind, und sich entsprechend verhalten. Man könnte sagen, dass die Zeilenelemente, der zusammenfassende Text und die Schaltflächen alle in der Lage sein sollten, automatisch auf ein aktiviertes oder deaktiviertes Kontrollkästchen zu reagieren. (Sehen Sie, was ich da gemacht habe?)

Sag mir, was du willst (was du wirklich, wirklich willst)

Um diese Seite mit React zu implementieren, können wir die Liste durch ein paar einfache Deklarationen von Fakten ersetzen

  • Es gibt eine Liste von Wahr/Falsch-Werten namens checkboxValues, die darstellt, welche Kästchen aktiviert sind.
    • Beispiel:  checkboxValues = [false, false, true, false]
    • Diese Liste repräsentiert die Wahrheit, dass wir vier Kontrollkästchen haben und dass das dritte aktiviert ist.
  • Für jeden Wert in checkboxValues gibt es ein Zeilenelement, das
    • eine CSS-Klasse namens .selected hat, wenn der Wert wahr ist, und
    • ein Kontrollkästchen enthält, das aktiviert ist, wenn der Wert wahr ist.
  • Es gibt ein zusammenfassendes Textelement, das den Text „{x} von {y} ausgewählt“ enthält, wobei {x} die Anzahl der wahren Werte in checkboxValues und {y} die Gesamtzahl der Werte in checkboxValues ist.
  • Es gibt eine Schaltfläche „Select All“, die aktiviert ist, wenn es falsche Werte in checkboxValues gibt.
  • Es gibt eine Schaltfläche „Select None“, die aktiviert ist, wenn es wahre Werte in checkboxValues gibt.
  • Wenn auf ein Kontrollkästchen geklickt wird, ändert sich sein entsprechender Wert in checkboxValues.
  • Wenn auf die Schaltfläche „Select All“ geklickt wird, werden alle Werte in checkboxValues auf true gesetzt.
  • Wenn auf die Schaltfläche „Select None“ geklickt wird, werden alle Werte in checkboxValues auf false gesetzt.

Sie werden feststellen, dass die letzten drei Punkte immer noch imperative Anweisungen sind („Wenn dies passiert, tue das“), aber das ist der einzige imperative Code, den wir schreiben müssen. Es sind drei Zeilen Code, und sie alle aktualisieren die einzige Quelle der Wahrheit. Der Rest dieser Punkte sind Deklarationen („es gibt ein…“), die jetzt direkt in die Definition der Seitenstruktur integriert sind. Um dies zu tun, schreiben wir unsere Elemente in einer speziellen JavaScript-Syntax, die von React namens JSX bereitgestellt wird und HTML ähnelt, aber JavaScript-Logik enthalten kann. Das gibt uns die Möglichkeit, Logik wie „if“ und „for each“ mit der HTML-Struktur zu mischen, sodass die Struktur je nach Inhalt von checkboxValues zu einem bestimmten Zeitpunkt unterschiedlich sein kann.

Hier ist dasselbe Einkaufslistenbeispiel wie oben, diesmal mit React implementiert

JSX ist definitiv seltsam. Als ich es zum ersten Mal sah, fühlte es sich einfach falsch an. Meine erste Reaktion war: „Was zum Teufel ist das? HTML gehört nicht in JavaScript!“ Ich war nicht allein. Dennoch ist es kein HTML, sondern JavaScript, das wie HTML aussieht. Es ist auch ziemlich mächtig.

Erinnern Sie sich an die Liste der 20 imperativen Anweisungen oben? Jetzt haben wir drei. Zum Preis der Definition unseres HTML innerhalb unseres JavaScript bekommen wir den Rest kostenlos. React macht sie einfach für uns, wann immer sich checkboxValues ändert.

Mit diesem Code ist es nun unmöglich, dass die Zusammenfassung nicht mit den Kontrollkästchen übereinstimmt, oder dass die Farbe einer Zeile falsch ist, oder dass eine Schaltfläche aktiviert ist, wenn sie deaktiviert sein sollte. Es gibt eine ganze Kategorie von Fehlern, die wir in unserer App jetzt unmöglich haben können: Quellen der Wahrheit, die nicht synchron sind. Alles fließt von der einzigen Quelle der Wahrheit ab, und wir Entwickler können weniger Code schreiben und nachts besser schlafen. Nun, JavaScript-Entwickler können das zumindest…

Es ist ein Kompromiss

Wenn Webanwendungen komplexer werden, hat die Aufrechterhaltung der klassischen Trennung der Belange zwischen HTML und JavaScript immer schmerzhaftere Kosten. HTML wurde ursprünglich für statische Dokumente entwickelt, und um diesen Dokumenten komplexere interaktive Funktionen hinzuzufügen, muss imperatives JavaScript mehr Dinge im Auge behalten und wird verwirrender und fragiler.

Der Vorteil: Vorhersagbarkeit, Wiederverwendbarkeit und Komposition

Die Möglichkeit, eine einzige Quelle der Wahrheit zu verwenden, ist der wichtigste Vorteil dieses Musters, aber der Kompromiss bietet uns auch andere Vorteile. Das Definieren von Elementen unserer Seite als JavaScript-Code bedeutet, dass wir Teile davon in wiederverwendbare Komponenten umwandeln können, wodurch wir vermeiden, dass wir dasselbe HTML an mehreren Stellen kopieren und einfügen. Wenn wir eine Komponente ändern müssen, können wir diese Änderung an einer Stelle vornehmen, und sie wird überall in unserer Anwendung aktualisiert (oder in vielen Anwendungen, wenn wir wiederverwendbare Komponenten für andere Teams veröffentlichen).

Wir können diese einfachen Komponenten nehmen und sie wie LEGO-Steine zusammensetzen, um komplexere und nützlichere Komponenten zu erstellen, ohne dass sie zu verwirrend werden, um damit zu arbeiten. Und wenn wir Komponenten verwenden, die von anderen erstellt wurden, können wir sie einfach aktualisieren, wenn sie Verbesserungen veröffentlichen oder Fehler beheben, ohne unseren Code umschreiben zu müssen.

Der Nachteil: Es ist überall JavaScript

All diese Vorteile haben ihren Preis. Es gibt gute Gründe, warum Menschen Wert auf die Trennung von HTML und JavaScript legen, und um diese anderen Vorteile zu erzielen, müssen wir sie zu einem kombinieren. Wie ich bereits erwähnt habe, verkompliziert die Abkehr von einfachen HTML-Dateien den Arbeitsablauf von jemandem, der sich vorher nicht um JavaScript kümmern musste. Es kann bedeuten, dass jemand, der zuvor selbst Änderungen an einer Anwendung vornehmen konnte, nun zusätzliche komplexe Fähigkeiten erlernen muss, um diese Autonomie aufrechtzuerhalten.

Es kann auch technische Nachteile geben. Zum Beispiel erwarten einige Tools wie Linter und Parser reguläres HTML, und einige imperative JavaScript-Plugins von Drittanbietern können schwieriger zu handhaben sein. Außerdem ist JavaScript nicht die am besten entwickelte Sprache; es ist nur das, was wir zufällig in unseren Webbrowsern haben. Neuere Tools und Funktionen verbessern es, aber es hat immer noch einige Fallstricke, die Sie kennen müssen, bevor Sie produktiv damit arbeiten können.

Ein weiteres potenzielles Problem ist, dass, wenn die semantische Struktur einer Seite in abstrakte Komponenten zerlegt wird, es für Entwickler leicht werden kann, nicht mehr darüber nachzudenken, welche tatsächlichen HTML-Elemente am Ende generiert werden. Spezifische HTML-Tags wie <section> und <aside> haben spezifische semantische Bedeutungen, die verloren gehen, wenn generische Tags wie <div> und <span> verwendet werden, selbst wenn sie auf einer Seite visuell gleich aussehen. Dies ist besonders wichtig für die Barrierefreiheit. Beispielsweise wirken sich diese Entscheidungen darauf aus, wie sich Screenreader-Software für sehbehinderte Benutzer verhält. Es mag nicht der aufregendste Teil sein, aber JavaScript-Entwickler sollten immer daran denken, dass semantisches HTML der wichtigste Teil einer Webseite ist.

Verwenden Sie es, wenn es Ihnen hilft, nicht, weil es „gerade angesagt ist“

Es ist zum Trend geworden, dass Entwickler bei jedem Projekt zu Frameworks greifen. Manche sind der Meinung, dass die Trennung von HTML und JavaScript überholt ist, aber das stimmt nicht. Für eine einfache statische Website, die nicht viel Benutzerinteraktion erfordert, lohnt sich der Aufwand nicht. Die enthusiastischeren React-Fans mögen mir hier widersprechen, aber wenn Ihr JavaScript nur dazu dient, eine nicht-interaktive Webseite zu erstellen, sollten Sie JavaScript nicht verwenden. JavaScript lädt nicht so schnell wie reguläres HTML. Wenn Sie also keine signifikante Verbesserung der Entwicklererfahrung oder Codezuverlässigkeit erzielen, richtet es mehr Schaden als Nutzen an.

Sie müssen auch nicht Ihre gesamte Website in React erstellen! Oder Vue! Oder Was auch immer! Viele Leute wissen das nicht, weil alle Tutorials da draußen zeigen, wie man React für das Ganze verwendet. Wenn Sie nur ein kleines, komplexes Widget auf einer ansonsten einfachen Website haben, können Sie React für diese eine Komponente verwenden. Sie müssen sich nicht immer um webpack oder Redux oder Gatsby oder all den anderen Mist kümmern, den Ihnen Leute als „Best Practices“ für Ihre React-App verkaufen wollen.

Für eine ausreichend komplexe Anwendung ist deklarative Programmierung den Aufwand absolut wert. Es ist ein Wendepunkt, der Entwickler auf der ganzen Welt in die Lage versetzt hat, erstaunliche, robuste und zuverlässige Software mit Zuversicht zu erstellen, ohne sich um die kleinen Dinge kümmern zu müssen. Ist React im Besonderen die bestmögliche Lösung für diese Probleme? Nein. Wird es einfach durch das nächste Ding ersetzt werden? Irgendwann. Aber deklarative Programmierung wird nicht verschwinden, und das nächste Ding wird es wahrscheinlich einfach besser machen.

Was habe ich über CSS-in-JS gehört?

Das fasse ich nicht an.