Virtual DOM verstehen und warum Svelte/SolidJS darauf verzichten
Wenhao Wang
Dev Intern · Leapcell

Einleitung
die Frontend-Entwicklung dynamisch durch eine neue Generation von JavaScript-Frameworks umgestaltet wurde. Jahrelang dominierten React und Vue mit ihrem innovativen Einsatz des Virtual DOM die Diskussion und versprachen Effizienz und entwicklerfreundliche Abstraktionen. Das Virtual DOM wurde zu einer fast allgegenwärtigen Optimierungstechnik, die weithin als Best Practice für die Verwaltung von UI-Updates akzeptiert wurde. Da sich das Frontend-Ökosystem jedoch ständig weiterentwickelt, fordern neue Akteure wie Svelte und SolidJS dieses etablierte Paradigma heraus. Sie argumentieren, dass das Virtual DOM, so clever es auch sein mag, eigene Komplexitäten und Overheads mit sich bringt, und schlagen alternative Strategien vor, die auf noch mehr Leistung und Einfachheit abzielen. Diese Diskussion ist nicht nur akademisch; sie hat erhebliche Auswirkungen darauf, wie wir Webanwendungen heute und in Zukunft erstellen und optimieren. Tauchen wir ein, was das Virtual DOM eigentlich ist und warum diese neueren Frameworks glauben, dass wir ohne es mehr erreichen können.
Kernkonzepte
Bevor wir uns den Alternativen zuwenden, ist es entscheidend, die zugrunde liegenden Technologien zu verstehen.
Das DOM (Document Object Model)
Das DOM ist eine Programmierschnittstelle für Webdokumente. Es repräsentiert die Seitenstruktur baumartig, wobei jeder Knoten ein Objekt ist, das einen Teil des Dokuments darstellt (z. B. ein HTML-Element, ein Attribut oder ein Textknoten). Die Interaktion mit dem DOM, wie das Hinzufügen, Entfernen oder Aktualisieren von Elementen, spiegelt Änderungen direkt auf dem Bildschirm des Benutzers wider. Die direkte Manipulation des DOM kann langsam sein, insbesondere wenn viele Änderungen in schneller Folge auftreten, da dies oft Layout-Neuberechnungen und Neudarstellungen durch den Browser auslöst.
Das Virtual DOM
Das Virtual DOM ist eine leichtgewichtige In-Memory-Darstellung des tatsächlichen DOM. Wenn sich der Status einer Anwendung ändert, wird ein neuer Virtual DOM-Baum erstellt. Dieser neue Baum wird dann in einem Prozess namens "Diffing" mit dem vorherigen verglichen. Der Diffing-Algorithmus identifiziert die minimale Menge an Änderungen, die erforderlich sind, um das tatsächliche DOM zu aktualisieren. Diese Änderungen werden dann gebündelt und in einer einzigen, optimierten Operation auf das reale DOM angewendet. Dadurch werden direkte DOM-Manipulationen minimiert, um die Leistung zu verbessern.
Betrachten Sie eine einfache Komponente, die einen Zähler anzeigt.
// React-ähnlicher Pseudocode function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Zähler: {count}</p> <button onClick={() => setCount(count + 1)}>Inkrementieren</button> </div> ); }
Wenn setCount aufgerufen wird, würde React:
- Die
Counter-Komponente neu rendern, was zu einem neuen Virtual DOM-Baum führt. - Diesen neuen Baum mit dem vorherigen abgleichen.
- Feststellen, dass sich nur der Textinhalt innerhalb des
<p>-Tags geändert hat. - Nur diesen spezifischen Textknoten im tatsächlichen DOM aktualisieren.
Dadurch muss der Browser nicht das gesamte <div> oder die <button>-Elemente neu rendern, was Rechenleistung spart.
Warum Virtual DOM unnötig sein könnte
Während das Virtual DOM eine deutliche Verbesserung gegenüber der direkten, unoptimierten DOM-Manipulation darstellt, argumentieren Frameworks wie Svelte und SolidJS, dass es sich um eine Abstraktion mit eigenen Kosten handelt und dass direktere, compilergesteuerte oder feingranulare Ansätze überlegene Ergebnisse erzielen können.
Svelte: Der Compiler-Ansatz
Svelte verfolgt einen fundamental anderen Ansatz. Es ist ein Compiler, der Ihren Komponentencode während des Build-Schritts in hochoptimiertes imperatives JavaScript umwandelt, anstatt ihn zur Laufzeit zu interpretieren. Das bedeutet, dass zur Laufzeit kein Virtual DOM vorhanden ist; Svelte-Komponenten manipulieren das DOM direkt, wenn sich der Zustand ändert.
Betrachten wir ein Svelte-Beispiel für denselben Zähler:
<!-- Counter.svelte --> <script> let count = 0; function increment() { count += 1; } </script> <div> <p>Zähler: {count}</p> <button on:click={increment}>Inkrementieren</button> </div>
Wenn Svelte diese Komponente kompiliert, generiert es im Wesentlichen JavaScript-Code, der ungefähr so aussieht (konzeptionell):
// Generierte Svelte-Ausgabe (konzeptionell) function create_fragment(ctx) { let p, t0, t1, t2, button; return { c() { // Elemente erstellen p = element('p'); t0 = text('Zähler: '); t1 = text(/*count*/ ctx[0]); t2 = space(); button = element('button'); button.textContent = 'Inkrementieren'; listener(button, 'click', /*increment*/ ctx[1]); }, m(target, anchor) { // Elemente einhängen insert(target, p, anchor); append(p, t0); append(p, t1); insert(target, t2, anchor); insert(target, button, anchor); }, p(ctx, [dirty]) { // Elemente aktualisieren if (dirty & /*count*/ 1) { // Wenn sich count geändert hat set_data(t1, /*count*/ ctx[0]); // Textknoten direkt aktualisieren } }, d(detaching) { // Elemente zerstören if (detaching) { detach(p); detach(t2); detach(button); } del_listener(button, 'click', /*increment*/ ctx[1]); } }; }
Wenn sich count ändert, aktualisiert der von Svelte generierte Code direkt den spezifischen Textknoten (t1), ohne dass ein neuer Baum generiert, verglichen und Patches angewendet werden müssen. Das "Diffing" geschieht zur Kompilierungszeit, da Svelte bereits genau weiß, welche Teile des DOM von welchen Zustandsvariablen abhängen. Dies eliminiert den Laufzeit-Overhead der Virtual DOM-Abgleichung.
SolidJS: Feingranulare Reaktivität ohne Virtual DOM
SolidJS lässt sich von reaktiven Programmierparadigmen wie Knockout.js oder MobX inspirieren, wendet sie aber auf JSX an. Es kompiliert JSX-Vorlagen in tatsächliche DOM-Knoten und wickelt dann Zustandsvariablen in "Signale" ein. Wenn sich ein Signal ändert, weiß SolidJS genau, welche Teile des DOM (oder andere Berechnungen) von diesem Signal abhängen und aktualisiert nur diese spezifischen Teile. Es vermeidet vollständig ein Virtual DOM, indem es direkte Einweg-Datenbindungen von reaktiven Primitiven zu den DOM-Elementen erstellt.
Betrachten Sie den Zähler erneut in SolidJS:
// SolidJS import { createSignal } from 'solid-js'; function Counter() { const [count, setCount] = createSignal(0); return ( <div> <p>Zähler: {count()}</p> {/* count() liest den Wert des Signals */} <button onClick={() => setCount(count() + 1)}>Inkrementieren</button> </div> ); }
Wenn setCount(count() + 1) aufgerufen wird, aktualisiert sich der Wert des count-Signals. SolidJS weiß, dass der count()-Aufruf innerhalb des Textinhalts des <p>-Tags von diesem Signal abhängt. Es aktualisiert dann direkt nur diesen Textknoten im DOM. Ähnlich wie bei Svelte gibt es keinen zwischengeschalteten Virtual DOM-Baum. Der Kompilierungsschritt von SolidJS für JSX erstellt effektiv eine Reihe von DOM-Operationen, die direkt durch Signaländerungen ausgelöst werden. Diese "feingranulare Reaktivität" bedeutet, dass nur die absolut notwendigen Updates stattfinden, was zu einer äußerst effizienten DOM-Manipulation führt.
Der wesentliche Unterschied besteht darin, dass React/Vue ein Virtual DOM verwenden, um tatsächliche DOM-Updates zu minimieren, während Svelte und SolidJS einen Schritt weiter gehen, indem sie das Virtual DOM ganz eliminieren. Svelte erreicht dies durch Kompilierungszeitoptimierung, indem es JavaScript generiert, das das DOM direkt manipuliert. SolidJS erreicht dies durch einen reaktiven Graphen, der Zustandsänderungen direkt auf bestimmte DOM-Knoten abbildet und ebenfalls in direkte DOM-Operationen kompiliert wird.
Fazit
das Virtual DOM eine geniale Lösung für die Herausforderungen ineffizienter DOM-Manipulationen war und die Frontend-Entwicklung revolutionierte. Mit der Weiterentwicklung von Frameworks sehen wir jedoch überzeugende Alternativen entstehen. Svelte und SolidJS zeigen, dass durch die Verlagerung der Arbeit von der Laufzeit zur Kompilierungszeit (Svelte) oder durch die Etablierung hochoptimierter, feingranularer reaktiver Verbindungen (SolidJS) der Overhead eines Virtual DOM und seines Abgleichsprozesses vollständig umgangen werden kann. Dies führt zu kleineren Bundle-Größen, schnellerer Laufzeit-Performance und oft zu einem einfacheren Denkmodell für Entwickler. Diese Frameworks verdeutlichen, dass das Virtual DOM uns zwar gute Dienste geleistet hat, aber nicht der einzige Weg ist, und auch nicht unbedingt der ultimative Weg, um schnelle und effiziente Webanwendungen zu erstellen. Die Zukunft der Frontend-Frameworks liegt möglicherweise in immer direkteren und optimierteren Wegen zur Interaktion mit den nativen Fähigkeiten des Browsers.

