Beispiele für Vanilla JSX
Was wäre, wenn JSX DOM-Elemente zurückgibt?
- Die Funktion
ClickMe erstellt einen Button und zeigt die Anzahl der Klicks an
- Bei jedem Klick auf den Button wird der Text aktualisiert
export default function ClickMe() {
let i = 0;
const el = Click me as HTMLButtonElement;
el.onclick = (e) => {
el.textContent = `Clicked ${++i} times`;
};
return el;
}
Wiederverwendbarkeit
- Die Komponente
ClickMe kann mehrfach verwendet werden, wobei jeweils ein eigener Zustand erhalten bleibt
import ClickMe from "./sample1.js";
export default () =>
;
Erstellen eines interaktiven DOM-Baums
- Mit den Klassen
TodoInput und TodoList lässt sich eine To-do-Liste verwalten
- Einträge können hinzugefügt und per Klick wieder entfernt werden
function TodoInput(attrs: { add: (v: string) => void }) {
const input = as HTMLInputElement;
input.placeholder = 'Add todo item...';
input.onkeydown = (e) => {
if (e.key === 'Enter') {
attrs.add(input.value);
input.value = '';
}
};
return input;
}
class TodoList {
ul = as HTMLUListElement;
add(v: string) {
const item =
- {v}
as HTMLLIElement;
item.onclick = () => item.remove();
this.ul.append(item);
}
}
export default () => {
const list = new TodoList();
list.add('foo');
list.add('bar');
return
list.add(v)} />
{list.ul}
;
};
Verarbeitung großer Datenmengen
- Die Funktion
FindNames verarbeitet große Datenmengen, filtert sie und zeigt die Ergebnisse an
- Je nach Eingabewert werden passende Einträge in Echtzeit aktualisiert
import { data } from "../fetch-dataset.js";
export default function FindNames() {
const status = as HTMLParagraphElement;
const results = as HTMLUListElement;
const input = as HTMLInputElement;
updateMatches();
function updateMatches() {
const matched = (data.entries().filter(([k]) => k.match(input.value)).toArray());
const matches = (Iterator.from(matched).map(match => ).take(30));
results.replaceChildren(...matches);
status.textContent = `${matched.length} / ${data.size}`;
}
return
{input}
{status}
{results}
;
}
function Item(attrs: { match: [string, number], regex: string }) {
const [name, count] = attrs.match;
const total = ({count});
return
-
{total}
;
}
function highlight(str: string, regex: string) {
if (!regex) return str;
const r = new RegExp(`(${regex})`, 'gi');
return str.replace(r, '$1');
}
Einführung in imlib
imlib ist eine Bibliothek, die für immaculatalibrary.com entwickelt wurde
- Sie wird zum Aufbau von minigamemaker.com und der Website verwendet, die Sie gerade lesen
- Sie wurde entwickelt, weil bestehende Ansätze für Zustand nicht ausreichten, und ist die bevorzugte Methode zum Erstellen von Apps
GN⁺-Zusammenfassung
- Dieser Artikel erklärt, wie sich mit JSX DOM-Elemente direkt erzeugen und interaktiv nutzen lassen
- Er zeigt, wie sich auch ohne klassisches Virtual DOM große Datenmengen effizient verarbeiten lassen
- Die Bibliothek
imlib ermöglicht eine einfache und intuitive App-Entwicklung
- Andere Projekte mit ähnlicher Funktionalität sind unter anderem React und Vue.js
1 Kommentare
Meinungen auf Hacker News
Wenn tatsächliche DOM-Nodes zurückgegeben werden, werden eine der großen Stärken von JSX und Nicht-JSX-Bibliotheken wie Lit – APIs im Immediate-Mode-Stil und das Modell
UI=f(state)– weitgehend entkräftet.Man muss nicht das echte DOM zurückgeben, sondern eine Beschreibung des DOM, damit man das Template mit neuem State wiederholt auswerten und das DOM, in das dieses Template gerendert wird, effizient aktualisieren kann.
Die Beispiele aktualisieren mit imperativen DOM-APIs wie
input.onkeydown = ...oderthis.ul.append(item), aber solchen Code zu vermeiden und Elemente im Template zu iterieren, ist vermutlich der Hauptvorteil eines virtuellen DOM.Das Problem beim virtuellen DOM ist allerdings, dass das Diffing langsam ist; das lässt sich mit Templates lösen, die – wie bei Lit – statische und dynamische Teile trennen.
Eher hat es den gegenteiligen Effekt: Man denkt, „ich kann große UIs beliebig neu rendern, das virtuelle DOM wird das schon erledigen“, während die tatsächliche Performance schlecht ist und die App-Struktur bereits so verhärtet ist, dass sie sich ohne Neuschreiben kaum noch reparieren lässt.
Ich habe denselben Verlauf in vier Teams gesehen und als Architekt auf Basis der Probleme, die man mit React bekommen würde, eine performancebewusste Vanilla-Struktur empfohlen, aber das wurde mit „es gibt viele Wege, das zu vermeiden“ abgetan.
Am Ende müssen viele das offenbar selbst schmerzhaft lernen.
createElement, und die beschriebenen Vorteile kommen aus Framework und Runtime.Kürzer gesagt: JSX ist eine alternative Syntax für Funktionsaufrufe, die nützliche Einsatzbereiche hat.
In einer früheren Firma haben wir JSX für Testdaten-Factories verwendet: Es sieht aus wie XML, aber beim Erzeugen der Elemente konnte man über ein Builder-Pattern kontextabhängige Entscheidungen für die endgültigen Daten treffen.
Das hatte nichts mit React oder dem DOM zu tun, und es bedeutete auch nicht, dass man es ohne JSX nicht deklarativ ausdrücken könnte.
Persönlich aktualisiere ich das DOM lieber imperativ. Man kann sehr fein steuern, was passiert, und es sehr effizient strukturieren.
Außerdem kann man App-Funktionen leichter hinzufügen, ändern, entfernen und anpassen, ohne sich an die starke Methodik anderer Leute anpassen zu müssen.
UI=f(state)– schwächt, aber das fühlt sich wie eine der leaky abstractions schlechthin in der gesamten Informatik an.Es gibt einen Grund, warum rund um React ein ganzes Ökosystem entstanden ist, das erklärt, wie man mehrfaches Rendern vermeidet oder unnötige Refreshes verhindert.
Ich habe dieses Projekt gebaut, weil mich der Zustand der Static-Site-Generatoren in den letzten zehn Jahren frustriert hat.
Da ich hauptsächlich statische Websites erstelle, wollte ich ein einfaches, intuitives Tool, und JSX schien gut zu passen; die übermäßige Komplexität von JSX-Frameworks wie React hatte ich aber satt.
Also habe ich einen Static-Site-Generator gebaut, der JSX als Strings rendert, und es war naheliegend, das auf den Browser auszuweiten, sodass JSX zu DOM-Elementen gerendert wird.
In manchen Fällen, besonders bei Layouts, passt das auch gut für gemeinsam genutzte Komponenten. Insgesamt bin ich mit dem Ergebnis zufrieden, auch wenn Teile ziemlich hacky sind und die IDE-Unterstützung besser sein könnte.
Zusätzlich passt dieser Ansatz auch gut zu SEO. Das war ein Problem, das meiner Meinung nach in anderen JSX-Frameworks nicht wirklich sauber gelöst ist.
In der Dokumentation wäre es wohl besser, „static site generator“ auszuschreiben, statt „SSG“ zu verwenden. Obwohl ich schon statische Website-Generatoren benutzt habe, wusste ich nicht, was SSG bedeutet, und musste eine KI fragen.
Ich kam von Vue und war überrascht: lit-html erledigt mit einfachen Vanilla-JS-Templates vieles von dem, was Vue-Templates tun, einschließlich Event-Binding.
Mit der VSCode-Lit-Erweiterung bekommt man sogar innerhalb der Templates Syntax-Highlighting und vollständige Typprüfung.
Ich bin darauf gestoßen, nachdem Marc Grabanski getwittert hatte, dass er lit-html zusammen mit VanillaJS statt Lit verwendet; nach eigenen Experimenten funktionierte es sehr gut. Es scheint dem, was du gerade lösen willst, ziemlich ähnlich zu sein.
Mit dem Template-Paket
lit-htmlkann man das meiste aus dem Template-Kapitel umsetzen: https://lit.dev/docs/templates/overview/Das geht auch ohne die Komponentenabstraktion von
lit-element: https://lit.dev/docs/libraries/standalone-templates/#renderi...Der Grund, warum solche „Was wäre, wenn“-Gedanken Spaß machen, ist, dass man die Ursprünge von JSX bis zu Facebooks XHP zurückverfolgen kann und XHP explizit von E4X inspiriert war, einem frühen JS-Standard, der ähnlich aussah und funktionierte wie die hier beschriebene Library.
[1] https://engineering.fb.com/2010/02/09/developer-tools/xhp-a-...
[2] https://en.m.wikipedia.org/wiki/ECMAScript_for_XML
Das ist auch der Grund, warum JSX über E4X hinausging, und danach gab es keine ernsthaften Vorschläge mehr für HTML-Templates in JS.
Trotzdem dürfte es möglich sein, die allgemeine Idee modern aufzubereiten und wiederzubeleben: https://github.com/WICG/webcomponents/issues/1069
for(of)stattfor eachbekommen hat.Der Grund, warum wir kein
for (:)bekommen haben, ist noch absurder: Es hätte mit:type-Annotationen kollidieren können, von denen einige TC39-Mitglieder glaubten, sie würden irgendwann auf magische Weise in die Sprache kommen.Ich habe einmal eine UI-Library gebaut, die auf der Idee basiert, dass JSX-Template-Ausdrücke echte DOM-Nodes erzeugen.
Sie bindet Modellobjekte an Eigenschaften und beseitigt so auch einen Teil des imperativen Event-Handler-Boilerplates. Wie man sich denken kann, halte ich das für eine gute Idee.
https://github.com/tomtheisen/mutraction
Man kann etwa mit
track({ clicks: 0 })ein Modell erstellen, bei einem Klickmodel.clickserhöhen und den Wert direkt im DOM widerspiegeln lassen.Hier ist auch VanJS erwähnenswert: https://vanjs.org/
Auch andere JSX-Libraries wie Solid.JS geben DOM-Nodes zurück, und ich finde es gut, dass diese Idee an Fahrt gewinnt.
Je näher wir an der Plattform arbeiten, die wir verwenden, desto besser, denke ich. Abstraktionsschichten können zwar nützlich sein, aber in der Praxis habe ich bisher keinen Bedarf gefunden, die Plattform zu verstecken.
Riesige Projekte wie Facebook könnten davon profitieren, aber in dieser Größenordnung habe ich nicht gearbeitet.
Die Beispiele sind cool, aber wichtig scheint mir, dass sie keine Komponenten-Props zeigen, die sich im Lauf der Zeit ändern können.
Diese Fähigkeit scheint gar nicht modelliert zu sein. Wenn man sie nicht braucht, ist das ein cleverer Ansatz, aber ich sehe nicht recht, wie er auf komplexere Apps skaliert.
Der Unterschied besteht lediglich darin, dass 10 Zeilen Code zum Erstellen und Konfigurieren von DOM-Elementen auf die übliche Weise knapper geschrieben werden.
Tatsächlich ist der wirklich interessante Teil dieses Frameworks fast alles andere: dass auf der Seite des Static-Site-Generators JSX als String zurückgegeben wird, dass rohe Source-Verzeichnisse eingelesen und zur SSG-Zeit
string|Buffermanipuliert werden, sowie ein selbst gebautes, sehr effizientes und schnelles Modulsystem auf Basis vonchokidarundswc.Der Teil, JSX als DOM zu verwenden, ist visuell am auffälligsten, in Wirklichkeit aber eher ein Partytrick.
Zum Beispiel ist der Source von vanillajsx.com sehr knapp, klar und kurz. Ich habe ihn heute komplett ohne Abhängigkeiten außer
imlibgeschrieben, und die JSX-as-DOM-Demo ist der am wenigsten innovative Teil: https://github.com/sdegutis/vanillajsx.com/tree/main/siteWenn man diese Art mag, bei der JSX konkrete DOM-Elemente zurückgibt, funktioniert Solid genau so und ergänzt das um eine vernünftige Reaktivitätsschicht.
setTimeoutodersetIntervalverhindert.Ich stimme allerdings zu, dass solche Projekte für kleine Use Cases oft gut funktionieren, in „Real-World“-Szenarien aber schnell auseinanderfallen.
Die Implementierung dürfte nicht allzu schwierig sein.
Das ist Vanilla TSX sehr ähnlich: https://github.com/wisercoder/uibuilder
Ein Beispiel für eine mit Vanilla TSX geschriebene App gibt es hier: https://github.com/wisercoder/eureka/tree/master/webapp/Clie...
Das erinnert an ActionScript 3, das XML im Kern der Sprache hatte.
Es war eine Sprache, die Spaß gemacht hat, aber sie ist bekanntlich daran gescheitert, ES4 zu werden. Am Ende hat es also über 10 Jahre gedauert, bis wir mit TypeScript und JSX an einem ähnlichen Punkt angekommen sind.
In Firefox 17, sechs Monate vor dem ersten React-Release, wurde es für Webseiten deaktiviert.
Ich frage mich, ob es noch jemanden gibt, bei dem das letzte Beispiel in Firefox nicht funktioniert. In Edge ging es, in Firefox schlug es fehl.
Uncaught (in promise) TypeError: Map.groupBy(...).entries().map is not a functionIch verstehe nicht so recht, warum JSX attraktiver sein soll als etwas wie unten:
h("div", {}, [ h("p", {}, "this is easy"), ...list.map((l) => h("li", {}, l)) ])Damit bekommt man Schleifen, Variablen-Interpolation usw. automatisch, ohne einen Compiler und neue Syntax zu bauen. Wäre schön, wenn das jemand erklären könnte.
Fairerweise nutzt man auch in JSX ganz normale Schleifen und Interpolation.
{}akzeptiert Vanilla-JS-Ausdrücke.Es braucht lediglich einen Kompilierschritt, der Element-Tags in
createElement-Aufrufe auflöst.Menschen verarbeiten Informationen unterschiedlich, aber dieses Beispiel hat so viel Interpunktion, dass es sehr schwer ist, es schnell zu erfassen.
Wenn man die Syntax nicht im Kopf selbst zerlegt, sieht man die erzeugte DOM-Struktur nicht; und die DOM-Struktur auf einen Blick zu verstehen ist viel wichtiger als die Frage, ob ein Compiler nötig ist.
Zur Einordnung: Ich bin selbst neurodivergent und hoffe, diese Erklärung hilft.
Es ähnelt dem, was man in den Browser-Entwicklertools sieht, daher lässt es sich leichter vergleichen und debuggen.
Aber wenn eine komplexe App Hunderte von Komponenten hat und diese anfangen, sich gegenseitig zu importieren und zu nutzen, wirkt die Schreibweise mit JSX-Tags wie eine einfache und leicht verständliche Syntax.
Die Gegenreaktion auf React kommt letztlich oft daher, dass es für einfache Dinge überdimensioniert ist — und das stimmt auch.
Aber je nach Person unterscheiden sich die Apps, die Komplexität und die Anforderungen; deshalb lässt sich nicht einfach sagen, eine Seite sei attraktiver. Es sind unterschiedliche Werkzeuge für unterschiedliche Probleme.