Effiziente Reaktivität für moderne Webentwicklung mit Signals
In diesem Beitrag bieten wir euch eine kleine Einführung in das Thema Signals und deren Bedeutung für die Webentwicklung. Entdeckt, wie ihr mit reaktiven Daten eure Anwendungen effizienter und dynamischer gestalten könnt.
Eines der Dinge, die ich an meiner Arbeit bei esveo schätze, ist die Möglichkeit, an interessanten Projekten zu arbeiten. Seit über einem Jahr ist eines dieser interessanten Projekte eine benutzerdefinierte, kalkulationsähnliche Anwendung zur Anzeige und Analyse verschiedener Datensätze.
Eine der wichtigsten Ideen hinter einer Tabellenkalkulation ist, dass alle Berechnungen immer live sind. Wenn Sie eine Zelle aktualisieren, müssen auch alle anderen Zellen auf der Grundlage der neuen Werte aktualisiert werden. Dies wird oft als Reaktivität bezeichnet, da alle Zellen auf Änderungen in anderen Zellen reagieren müssen.
Reaktivität ist jedoch nicht nur auf Tabellenkalkulationen beschränkt - sie ist ein wichtiger Bestandteil der Webentwicklung. Wenn eine Anwendung neue Daten von einer API lädt, muss sie wissen, wie sie die Benutzeroberfläche aktualisieren muss, um die neuen Daten anzuzeigen. Text könnte aktualisiert werden, Elemente in einem Diagramm könnten sich verschieben, neue Datenzeilen könnten hinzugefügt werden, eine Schaltfläche, die zuvor deaktiviert war, könnte aktiviert werden, usw.
In beiden Fällen möchten wir als Entwickler die natürlichen Abhängigkeiten unserer Anwendung verfolgen können. Wenn in einer Tabellenkalkulation die Zelle B5 von der Zelle A1 abhängt, möchte ich sicherstellen, dass B5 jedes Mal aktualisiert wird, wenn sich A1 ändert - aber auch, dass sie nicht aktualisiert wird, wenn eine andere, irrelevante Zelle aktualisiert wird. In einer Frontend-Anwendung möchte ich, wenn sich der Zustand ändert, alle veralteten DOM-Elemente aktualisieren, aber ich möchte nicht meine gesamte Anwendung neu rendern, wenn das meiste davon gleich geblieben ist.
Im Fall unseres Tabellenkalkulationsprogramms haben wir festgestellt, dass dasselbe Tool für beide Fälle hilfreich war: Signals.
Was sind Signals?
Ein Signal ist ein Wrapper um einige Daten - z. B. eine Zahl oder ein Objekt - der es Euch ermöglicht, auf Änderungen dieser Daten zu reagieren. Signals gibt es in verschiedenen Stilen und APIs - viele Frontend-Frameworks enthalten ihre eigene Signalbibliothek - aber die Erstellung eines Signals könnte etwa so aussehen:
const counter = signal(0)
Um auf Änderungen des Signals zu reagieren, verwenden wir eine Effektfunktion. Wenn wir zum Beispiel nur etwas in die Konsole schreiben wollen, wenn sich ein Wert ändert, könnten wir dies tun:
effect(() => {
console.log(`counter is ${counter()}`);
})
// -> "counter is 0"
Die counter()
Funktion gibt den Wert des zuvor erstellten Signals zurück. Gleichzeitig prüft die effect()
Funktion, welche Signals innerhalb des Körpers des Effekts verwendet wurden. Das bedeutet, dass wir Abhängigkeiten automatisch verfolgen - ohne die Abhängigkeits-Arrays von React oder manuelle Aufrufe von .subscribe()
.
Der Effekt wird immer einmal sofort ausgeführt - dies ist notwendig, damit die anfängliche Verfolgung eingerichtet werden kann und der Effekt weiß, auf welche Signals er hört. Danach können wir ihn erneut ausführen, indem wir den Wert von counter
aktualisieren:
counter.set(1);
// -> "counter is 1"
Hier schreibt der Effekt etwas in die Konsole, aber wir könnten auch einen anderen Effekt haben, der das DOM aktualisiert - Text ändert, Elemente ausblendet, eine neue Zeile hinzufügt usw. In der Praxis wird die Aktualisierung des DOM in der Regel mit einem Framework wie SolidJS, Vue oder Angular durchgeführt, das dafür sorgt, dass alle Aktualisierungen zum richtigen Zeitpunkt geplant werden, um Leistungsprobleme zu vermeiden. Unter der Haube verwenden diese Frameworks jedoch Signals und Effekte, um diese Seiteneffekte zu verwalten.
Warum sind Signals so spannend?
Signals sind im Moment in aller Munde. Sowohl das Angular- als auch das Svelte-Framework haben Signals eingeführt. Vue verwendet Signals schon seit einiger Zeit, ersetzt aber derzeit sein Rendering-System durch ein System, das Signals verwendet, um die Größe der Pakete massiv zu reduzieren und die Leistung zu verbessern. SolidJS, das wohl Pionierarbeit bei der Verwendung von Signals in modernen Frameworks geleistet hat, hat gerade SolidStart veröffentlicht.
Vielleicht noch aufregender ist, dass TC39, das Komitee, das entscheidet, welche neuen Funktionen zu Javascript hinzugefügt werden sollen, damit begonnen hat, einen Vorschlag zu prüfen, Javascript-native Signals hinzuzufügen. Das bedeutet, dass alle verschiedenen Frameworks, die Signals verwenden, dieselbe Grundlage nutzen können. Das könnte auch die Leistung und die Debugging-Möglichkeiten verbessern (stellt Euch vor, der Browser könnte Euch ein Diagramm zeigen, wie Euer gesamter Zustand umgewandelt und angezeigt wird) und Webanwendungen kleiner und leichter machen.
Ich persönlich finde, dass Signals wirklich einfach zu handhaben sind, und ich denke, das ist ein wichtiger Grund für die Begeisterung. In React kann ich mit dem Zustand von Komponenten, separaten globalen Speichern wie Redux oder MobX und anderen verschiedenen Zustandswerten jonglieren. Aber mit z.B. SolidJS kann ich Signals für fast alles verwenden. Es funktioniert als globaler Speicher, es funktioniert mit dem Komponentenzustand und es funktioniert überall dazwischen - alles mit automatischer Abhängigkeitsverfolgung und feinkörnigen, effizienten DOM-Aktualisierungen.
![Paul Dittrich](https://quarxi.mo.cloudinary.net/default/f/156829/648x648/8f14c46daa/paul.jpeg?tx=f_auto,h_100,w_100)
![Andreas Roth](https://quarxi.mo.cloudinary.net/default/f/156829/2000x2000/4ffa9c04d2/andreas.jpg?tx=f_auto,h_100,w_100)
![Andreas Roth](https://quarxi.mo.cloudinary.net/default/f/156829/2000x2000/4ffa9c04d2/andreas.jpg?tx=f_auto,h_100,w_100)
![Paul Dittrich](https://quarxi.mo.cloudinary.net/default/f/156829/648x648/8f14c46daa/paul.jpeg?tx=f_auto,h_100,w_100)
![Paul Dittrich](https://quarxi.mo.cloudinary.net/default/f/156829/648x648/8f14c46daa/paul.jpeg?tx=f_auto,h_100,w_100)
![Dominic Arnold](https://quarxi.mo.cloudinary.net/default/f/156829/1162x1162/db6bed3a36/dom_square.jpg?tx=f_auto,h_100,w_100)
![Dominic Arnold](https://quarxi.mo.cloudinary.net/default/f/156829/1162x1162/db6bed3a36/dom_square.jpg?tx=f_auto,h_100,w_100)
![Dominic Arnold](https://quarxi.mo.cloudinary.net/default/f/156829/1162x1162/db6bed3a36/dom_square.jpg?tx=f_auto,h_100,w_100)
![Yujin Lu](https://quarxi.mo.cloudinary.net/default/f/156829/969x1023/4ce0dc47de/yujin-lu.jpeg?tx=f_auto,h_100,w_100)
![Paul Dittrich](https://quarxi.mo.cloudinary.net/default/f/156829/648x648/8f14c46daa/paul.jpeg?tx=f_auto,h_100,w_100)
![Philipp Dehler](https://quarxi.mo.cloudinary.net/default/f/156829/800x800/1c2ba92120/1630094546364.jpeg?tx=f_auto,h_100,w_100)
![Dominic Arnold](https://quarxi.mo.cloudinary.net/default/f/156829/1162x1162/db6bed3a36/dom_square.jpg?tx=f_auto,h_100,w_100)