3 Punkte von GN⁺ 2024-03-29 | 1 Kommentare | Auf WhatsApp teilen
  • Das Rust-GUI-Framework Dioxus 0.5 vereinfacht den Entwicklungsablauf für Web, Desktop, Mobile und Fullstack deutlich, vor allem durch die Neuschreibung von dioxus-core und die Entfernung von unsafe Code
  • Zwischen 0.4.3 und 0.5.0 kamen mehr als 100.000 Zeilen Code und über 1.400 Commits hinzu; der neue Core wurde so umgebaut, dass Lifetimes und die Abhängigkeit von Scope entfallen
  • Die bisherige, auf use_state und use_ref zentrierte Zustandsverwaltung wird durch eine kopierbare Signal-API ersetzt, wodurch in Event-Handlern und Futures weniger wiederholtes Clone nötig ist
  • Mit einem einzigen dioxus::launch und dem Ablauf dx serve --platform lassen sich Web-, Desktop- und Fullstack-Apps starten; die CLI übergibt automatisch die zur Zielplattform passenden Build-Features
  • Asset-Hot-Reload, eine Überarbeitung der Events, Verbesserungen beim Desktop-Rendering und Streaming für Server Functions erweitern zusammen den Einsatzbereich einer einheitlichen Rust-Codebasis

Release-Ausrichtung von Dioxus 0.5

  • Dioxus ist eine Bibliothek zum Erstellen von GUIs mit Rust und wird für die Bereitstellung von Web-, Desktop- und Mobile-Apps eingesetzt
  • Das Release 0.5 wurde mit dem Ziel entworfen, die von der Community gewünschte Vereinfachung, Robustheit und höhere Ausgereiftheit zu erreichen
  • Die wichtigsten Änderungen sind:
    • vollständige Neuschreibung von dioxus-core und Entfernung von unsafe Code
    • Einführung einer auf Signal basierenden API anstelle von use_state und use_ref
    • Entfernung aller Lifetimes und des Zustands cx: Scope
    • eine einzige launch-Funktion zum Starten von Apps auf allen Plattformen
    • Asset-Hot-Reload mit Unterstützung für Tailwind und Vanilla CSS
    • Überarbeitung des Event-Systems mit Zugriff auf native WebSys-Event-Typen
    • Integration von Error Boundary, Server Future und Suspense
    • 5-fache Verbesserung der Desktop-Reconciliation
    • Streaming für Server Functions und Fullstack-Hot-Reload
  • Für Nutzer, die von Dioxus 0.4 aktualisieren, steht ein migration guide bereit

Entfernung von Lifetimes und Scope

  • Von Dioxus 0.1 bis 0.4 hatten Werte innerhalb von Komponenten die Lifetime 'bump; dadurch konnten Hooks, Props und Scope in Event-Listenern ohne Klonen verwendet werden
  • In Event-Handlern funktionierte das meist gut, aber Futures in Dioxus mussten 'static sein, sodass Werte vor dem Verschieben in eine Future geklont werden mussten
  • Bei Lifetime-Fehlern konnten verwirrende Meldungen entstehen, etwa dass cx länger als 'static leben müsse, statt den eigentlichen Hook zu benennen
  • Dioxus 0.5 entfernt Scope und die 'bump-Lifetime und macht Element lifetime-frei
  • Komponenten können Props nun direkt ohne Scope-Parameter entgegennehmen
    • Beispiel: fn MyComponent(name: String) -> Element
  • Runtime-Funktionen lassen sich nicht nur innerhalb von Komponenten, sondern auch direkt in Futures und Event-Handlern verwenden
  • Da Element nun 'static ist, kann es auch innerhalb von Hooks genutzt oder über die Context-API bereitgestellt werden
  • Diese Änderung schafft eine Grundlage, mit der sich APIs wie virtuelle Listen oder Offscreen-Rendering leichter implementieren lassen

Entfernung von unsafe aus dioxus-core

  • Die Entfernung der 'bump-Lifetime und von Scope eröffnete die Möglichkeit, unsafe Code innerhalb von Dioxus zu reduzieren
  • dioxus-core 0.5 enthält keinen unsafe Code
  • In einigen Abhängigkeiten verbleibt eine kleine Menge unsafe Code; das Dioxus-Team plant, diese im Verlauf des 0.5-Release-Zyklus zu entfernen
  • Der verbleibende unsafe Code wird entweder als schlicht entfernbar oder als wegen FFI notwendig eingestuft

Signal-basierte Zustandsverwaltung

  • Dioxus 0.5 führt Signal als zentrale Zustands-Primitive für Komponenten ein
  • Signal hat gegenüber dem bisherigen use_state und use_ref zwei Vorteile
    • Es ist immer Copy
    • Es erfordert keine manuelle Subscription
  • Copy-Zustand

    • Signal<T> ist Copy, auch wenn der enthaltene Wert T nicht Copy ist
    • Dieses Verhalten wird durch die ohne unsafe implementierte Crate generational-box ermöglicht
    • Bei Bedarf kann ein Signal Send+Sync gemacht werden, sodass es zwischen Threads verschoben werden kann
    • Durch die Kombination aus Copy-Zustand, Send+Sync-Signal und statischen Komponenten lässt sich Zustand leicht an die benötigten Stellen verschieben, etwa in Futures, Event-Handler oder Threads
    • Die Speicherverwendung ist fast identisch mit 0.4, aber explizites Clone ist nicht mehr nötig
  • Intelligente Subscriptions

    • Signal entscheidet feiner abgestimmt, welche Komponente neu ausgeführt werden soll, wenn sich ein Wert ändert
    • Eine Komponente wird nur dann erneut ausgeführt, wenn sie den Signal-Wert gelesen hat
    • Lesezugriffe in async Tasks oder Event-Handlern werden nicht als Subscription für eine erneute Komponentenausführung behandelt
    • Wenn ein Parent per Button-Klick ein Signal ändert, den Wert aber nicht direkt liest, und nur das Child den Wert liest, wird nur das Child neu gerendert
    • Durch diese Struktur wird Fermi, eine separate Crate für Zustandsverwaltung, nicht mehr benötigt
    • Fermi stellte eine use_state-ähnliche API bereit, die static als Schlüssel verwendete
    • In Dioxus 0.5 kann GlobalSignal in einem static abgelegt und wie ein normales Signal genutzt werden
    • Signal funktioniert auch mit der Context-API, sodass Zustand ohne separaten use_shared_state-Hook zwischen Komponenten geteilt werden kann
    • Wenn ein Signal in Hooks wie use_future oder use_memo gelesen wird, wird dieses Signal automatisch zu den Hook-Abhängigkeiten hinzugefügt

CSS- und Asset-Hot-Reload

  • Dioxus 0.5 implementiert im Rahmen der Überarbeitung des Asset-Systems Hot-Reload für CSS-Dateien im Asset-Verzeichnis
  • Wenn eine CSS-Datei in RSX vorkommt, überwacht die dx-CLI diese Datei und streamt Updates sofort in die laufende App
  • Unterstützt werden Web, Desktop und Fullstack; Mobile-Unterstützung soll in einem künftigen Mobile-fokussierten Update folgen
  • In Kombination mit dem Tailwind-Watcher wird auch Tailwind-CSS-Hot-Reload unterstützt
  • In VSCode lassen sich über die custom regex extension auch Hinweise für Tailwind-Klassen erhalten
  • Änderungen können gleichzeitig an mehrere Geräte gestreamt werden, sodass Hot-Reload auf allen Zielgeräten erfolgt

Überarbeitung des Event-Systems

  • Seit dem ersten Release nutzt Dioxus ein Synthetic-Event-System, um eine Cross-Platform-Event-API bereitzustellen
  • Synthetic Events sind hilfreich für plattformübergreifendes Event-Verhalten und Netzwerkserialisierung, haben aber auch Grenzen
  • Dioxus 0.5 legt die zugrunde liegenden Event-Typen der jeweiligen Plattform offen und stellt zusätzlich Traits für die Cross-Platform-API bereit
  • Diese Änderung hat zwei Vorteile
    • Benötigte Informationen können direkt aus dem Plattform-Event-Typ gewonnen oder an andere Bibliotheken weitergegeben werden
    • Event-Code, den eine App nicht nutzt, kann per Bundle Splitting ausgelagert werden
  • Im Hello-World-Beispiel sinkt die gzipped Größe um etwa 25 %
  • Tipps für kleine Bundles finden sich im Dioxus optimization guide

Cross-Platform-API zum Starten

  • Dioxus 0.5 führt eine neue Cross-Platform-API zum Starten von Apps ein
  • Statt separate Renderer-Pakete zu importieren, genügt es, in der Crate dioxus ein Feature zu aktivieren und die Funktion launch aus dem Prelude aufzurufen
  • Eine einzelne App kann für folgende Plattformen ausgeführt werden
    • Desktop: dx serve --platform desktop
    • SPA Web: dx serve --platform web
    • Fullstack: dx serve --platform fullstack
  • Die CLI übergibt je nach Zielplattform automatisch die passenden Build-Features

Asset-System-Beta und Manganis

  • In Dioxus und Web-Apps können Asset-Pfade leicht veralten, Links können sich zwischen Desktop und Web unterscheiden, und Assets, die in das Bundle aufgenommen werden sollen, müssen manuell hinzugefügt werden
  • Assets können außerdem zu Performance-Engpässen werden
  • Im Beispiel des Dioxus-Mobile-Guides brauchte Version 0.4 sieben Sekunden zum Laden und übertrug 9 MB an Ressourcen
  • Der Mobile-Guide für 0.5 lädt mit denselben Bildern in unter einer Sekunde und benötigt nur ein Drittel der Ressourcen
  • Dioxus 0.5 führt das neue Asset-System manganis ein
    • Es ist in die CLI integriert und prüft, bündelt und optimiert App-Assets
    • Da die API noch instabil ist, wird es als separate Crate veröffentlicht
    • Wenn Assets mit dem Makro mg! umschlossen werden, erkennt die CLI sie automatisch
    • Weitere Details stehen in den manganis docs
  • Während des 0.5-Release-Zyklus ist geplant, auch für Manganis-Assets Hot-Reload hinzuzufügen

5-fache Verbesserung des Desktop-Renderings

  • Dioxus nutzt mehrere Optimierungen, um Rendering-Diffs schnell zu machen
  • Templates sorgen dafür, dass der Diff für statische Teile des rsx!-Makros übersprungen wird
  • In Dioxus Web werden DOM-Änderungen aus Rust heraus über sledgehammer schnell angewendet
  • Dioxus 0.5 nutzt denselben Ansatz auch für das Anwenden von Änderungen über das Netzwerk
  • Desktop- und LiveView-Renderer kommunizieren Änderungen nicht mehr per JSON, sondern über ein binäres Protokoll
  • Bei renderlastigen Aufgaben reduziert der neue Renderer die Zeit zum Anwenden von Änderungen im Browser auf ein Fünftel und die Latenz auf die Hälfte
  • Ein Benchmark, bei dem der Renderer in Dioxus 0.4 ständig hängen blieb, läuft in Dioxus 0.5 flüssig

Komfortfunktionen beim Schreiben von Komponenten

  • Dioxus 0.5 unterstützt das Erweitern bestimmter Elemente und das Ausbreiten von Attributen auf ein Element
    • Beispiel: Eine ImgPlus-Komponente, die Attribute des img-Elements erweitert, kann normale img-Attribute wie width, height und src unverändert entgegennehmen
  • Beim Übergeben von Werten an Attribute und Komponenten kann die Kurzschreibweise für Struct-Initialisierung verwendet werden
    • Statt class: class kann man class schreiben
  • Kurzattribute funktionieren für alles, was IntoAttribute implementiert; auch Signal profitiert davon
  • Signal-Attribute überspringen das Diffing noch nicht, sollen aber im Verlauf des 0.5-Release-Zyklus als Performance-Optimierung ergänzt werden
  • Über mehrere Zeilen verteilte Attribute können zusammengeführt werden
    • Wenn demselben class-Attribut bedingte Werte hinzugefügt werden, werden sie mit Leerzeichen als Trenner zusammengeführt
    • Das ist wichtig für Bibliotheken wie Tailwind, die Compile-Time-Parsing benötigen und gleichzeitig dynamische Verarbeitung zur Laufzeit brauchen
    • Diese Syntax ist in den Tailwind-Compiler integriert und entfernt den Runtime-Overhead von Bibliotheken wie tailwind-merge

Streaming für Server Functions und Fullstack

  • Dioxus 0.5 unterstützt die aktuelle server functions crate, die Streaming-Daten unterstützt
  • Server Functions können Daten zum Client streamen oder Daten vom Client zum Server streamen
  • Eine Streaming-Server-Function lässt sich erstellen, indem man einen Ausgabetyp definiert und aus der Server Function TextStream zurückgibt
  • Das eignet sich, um Clients während lang laufender Aufgaben zu aktualisieren
  • Es gibt ein Beispiel, das mit Kalosm und einem lokalen LLM auf gewöhnlicher Hardware eine ähnliche Funktionalität wie ein OpenAI-ChatGPT-Endpoint bereitstellt
  • Die CLI unterstützt nun die Plattform fullstack und bietet Hot-Reload sowie parallele Builds für Client und Server
    • dx serve
    • dx serve --platform fullstack

LiveView, Asset-Handler und Dateiverarbeitung

  • In Dioxus 0.5 funktioniert der Router standardmäßig in LiveView-Apps
  • Dioxus Desktop unterstützt Custom Asset Handler
  • Custom Asset Handler ermöglichen es, Daten aus Rust-Code ohne Umweg über JavaScript effizient an den Browser zu streamen
  • Sie eignen sich für bandbreitenintensive Kommunikation wie Video-Streaming
  • gstreamer- oder webrtc-Daten können direkt an die WebView übergeben werden, wodurch die Notwendigkeit sinkt, Frames selbst zu kodieren und zu dekodieren
  • Auch Datei-Drops auf dem Desktop sind nativ in das Event-System integriert

Fehlerbehandlung

  • Dioxus erleichtert über Error Boundary und das throw-Trait die Fehlerbehandlung in übergeordneten Komponenten
  • Der throw-Ansatz kombiniert die Vorteile von Fehlerzustand und frühem Return
  • Bei einem Result-Typ, der Debug implementiert, kann throw aufgerufen werden, um ihn in einen Fehlerzustand umzuwandeln, und mit ? früh zurückgegeben werden
  • Die Komponente ErrorBoundary rendert eine andere Komponente, wenn in ihren Children ein Fehler geworfen wurde
  • ErrorBoundary kann verschachtelt werden, sodass Fehler auf mehreren Ebenen einer App abgefangen werden können
  • Dieses Muster ist nützlich, um bei nicht behebbaren Fehlern einen globalen Fehlerzustand zu behandeln, statt zu panicken oder für jeden Fehler den Zustand manuell zu verwalten

Developer Experience und Templates

  • Dioxus führte Hot-Reload in 0.3 ein, ergänzte es in 0.4 für Desktop und aktiviert es in 0.5 standardmäßig
  • Wenn eine App mit dx serve gestartet wird, ist Hot-Reload im Entwicklungsmodus standardmäßig eingeschaltet
  • Auch wenn Hot-Reload in Desktop-Apps nicht möglich ist und eine vollständige Neukompilierung nötig wird, bleibt der Zustand geöffneter Fenster erhalten und wird wiederhergestellt
    • Größe und Position des App-Fensters bleiben erhalten
    • Das reduziert Situationen, in denen die App bei jeder Bearbeitung den gesamten Bildschirm blockiert
  • Die neuen Templates sind so organisiert, dass Web-, Desktop-, Mobile-, TUI- und Fullstack-Apps mit einem einzigen Befehl erstellt werden können
  • Die Standard-App von dx new wurde stärker an create-react-app angelehnt
    • Sie enthält Assets, CSS und grundlegende Deployment-Einstellungen
    • Sie enthält Links zu nützlichen Ressourcen wie dioxus-std, VSCode Extension, Dokumentation und Tutorials

Dioxus Community und Ökosystem

  • Die Dioxus Community hat wichtige Ökosystem-Crates für das 0.5-Release aktualisiert
  • Crates wie icons, charts und eine Dioxus-spezifische Standardbibliothek wurden so vorbereitet, dass sie zum Start von 0.5 sofort nutzbar sind
  • Das Projekt Dioxus Community ist eine neue GitHub-Organisation, die wichtige Crates aktuell halten soll, auch wenn ursprüngliche Maintainer aussteigen
  • Wer Bibliotheken für Dioxus entwickelt, kann von Dioxus-Seite Unterstützung bei der Wartung erhalten; Ziel ist, sie faktisch mit „Tier 2“-Support zu betreuen

Geplante Funktionen

  • Zu den Plänen nach 0.5 gehören:
    • Stabilisierung und tiefere Integration des Asset-Systems
    • direktes Bundle Splitting der ausgegebenen .wasm zusammen mit Lazy Components
    • Islands und resumable Interactivity, Signal-Serialisierung
    • Zusammenführung von Server Components und LiveView in Fullstack
    • verbesserte Devtools und ein Test-Framework
    • umfassende Überarbeitung von Mobile
    • Fullstack-Überarbeitung mit WebSocket, SSE, Progressive Forms und mehr

Vorschau auf Servo-basiertes Dioxus-Blitz

  • „Blitz 2.0“ von Dioxus-Blitz zielt darauf ab, Servo zu integrieren und mit derselben CSS-Engine wie Firefox natives Rendering über WGPU zu ermöglichen
  • Nico Burns, der die Layout-Bibliothek Taffy entwickelt hat, ist in Vollzeit dazugestoßen, um diese Arbeit voranzutreiben
  • In der Demo wird google.com mit 900 FPS auf der GPU gerendert
  • Die aktuelle Implementierung ist noch nicht vollständig, und auch das Rendering von google.com wirkt noch etwas unbeholfen, nähert sich aber schnell einem nutzbaren Niveau
  • Das Repository ist unter https://github.com/jkelleyrtp/stylo-dioxus zu finden

Mitwirken

  • Das Dioxus-Projekt wünscht sich Beiträge in folgenden Bereichen
    • Übersetzung der Dokumentation
    • Bearbeitung von „Good First Issues“
    • Verbesserung der Dokumentation
    • Beiträge zur CLI
    • Beantwortung von Fragen in der Discord-Community
  • Das Dioxus-Team bedankt sich für die Unterstützung der Community im restlichen Jahr 2024 und bittet um Hilfe dabei, die App-Entwicklung zu verändern

1 Kommentare

 
GN⁺ 2024-03-29
Meinungen auf Hacker News
  • Ich habe letztes Jahr mit Dioxus den Mastodon-Client Ebou gebaut. Insgesamt war es eine gute Erfahrung, aber es fehlte auch vieles.
    Als ich mit der Arbeit begann, war es Version 0.2, und mit den Änderungen in 0.5 scheint fast die gesamte Komplexität, mit der ich damals zu kämpfen hatte, verschwunden zu sein. Ich habe es noch nicht selbst ausprobiert, aber durch den Wegfall der Lifetimes und die geringere Notwendigkeit, ständig zu klonen, dürfte es deutlich angenehmer werden.
    • Mich würde interessieren, wie du Dioxus ausgewählt hast.
      Es gibt ziemlich viele Rust-Frameworks, die native reaktive UIs für Deployment auf Desktop, Web/Wasm, Mobile usw. ermöglichen wollen (https://github.com/flosse/rust-web-framework-comparison), und ich mache mir Sorgen, dass man bei einer falschen Wahl am Ende ein aufgegebenes Framework pflegen oder eine schmerzhafte Migration durchführen muss.
      Bei Rust-HTTP-Server-Frameworks gab es ähnlich viele, und inzwischen scheinen Axum, Actix und Rocket vorn zu liegen; zugleich wirkt es so, als bewege sich die Community in Richtung Axum, sodass ich mich frage, ob die Wahl von Actix ein Fehler war. Ich wüsste gern, ob es Anzeichen dafür gibt, dass Dioxus sich durchsetzen wird, und ob es neben großer Community, YC-Unterstützung und Momentum weitere Kennzahlen gibt, die dafür sprechen, es jetzt zu wählen.
      Außerdem würde ich gern wissen, ob Leptos und Yew ebenfalls wichtige Konkurrenten sind und aus welchen Gründen sie besser oder schlechter als Dioxus sind. Unser Unternehmen hat stark in Rust, Actix und Bevy investiert, und künftig wollen wir Frameworks wie Bevy und Dioxus kombinieren, um native Desktop- und Mobile-Clients zu bauen.
      Und ich frage mich auch, ob der Name Dioxus von Deoxys aus Pokémon stammt. Das Logo fühlt sich so an, aber in der Codebasis gibt es keine Pokémon-Referenzen.
  • Der Aussage „insgesamt gut, aber es fehlte vieles“ stimme ich zu.
    Vor etwa neun Monaten habe ich mit Dioxus ein GUI-Frontend für sshfs gebaut, und bis zu dem Punkt, an dem ich gegen eine Wand lief, weil die Entwickler irgendeine Funktion noch nicht fertiggestellt hatten, wirkte es wie ein wirklich hervorragendes GUI-Framework.
    State zwischen verschiedenen Kontexten zu teilen, ist gelegentlich ebenfalls schmerzhaft, aber das war bei jedem GUI-Framework so, das ich je benutzt habe, unabhängig von Sprache oder zugrunde liegender Technik. Dioxus 0.5 sieht hier nach einem großen Fortschritt aus. Mein Blog rendert einen beträchtlichen Teil des HTML mit Dioxus, und da ich es nicht bis an seine Grenzen ausreize, funktioniert es sehr gut.
  • Ich beobachte Dioxus mit Interesse, hatte aber noch keine Gelegenheit, es auszuprobieren.
    Allerdings finde ich die Lösung zur Entfernung der Lifetimes etwas merkwürdig. generational-box wirkt auf mich wie eine Art Garbage Collector für Arme, und ich frage mich, wie stark die Performance-Auswirkungen waren.
    Außerdem ist der Link [generational-box]([https://crates.io/crates/generational-box](<https://crates.io/crates/generational-box>;)) kaputt.
    • Es ist tatsächlich ein Garbage Collector für Arme, aber die Speichersemantik ist exakt dieselbe wie in der vorherigen Version.
      use_hook besitzt einen Wert für die Lebensdauer der Komponente, daher wird dieser Wert gedroppt, wenn die Komponente verschwindet. Alle Signals verwenden weiterhin use_hook, daher ist auch ihre Lebensdauer identisch. GenerationalBox::new() außerhalb von use_hook aufzurufen, wird normalerweise nicht empfohlen, deshalb gibt es keine Performance-Auswirkungen.
      Wenn man in einer Schleife massenhaft GenerationalBox::new() aufruft, bleibt dieser Müll zwar bis zum Drop der Komponente erhalten, aber meistens wird man in eine Map oder einen Vec pushen bzw. daraus poppen, und dann gilt die normale Speichersemantik.
    • Das ist im Grunde Automatic Reference Counting: https://en.wikipedia.org/wiki/Automatic_Reference_Counting
  • Ich bin kein Rust-Programmierer, aber mich interessiert, wie die Crate generational-box funktioniert.
    Ich verstehe, dass es eine Art Arena-Allokation ist, aber ich verstehe nicht, wie sie „Kopieren ohne Kopieren“ unterstützt und warum die Erklärung sicher ist, dass intern eine RefCell-Arena mit Generationen angelegt wird und GenerationalBox Copy ist.
    Ich verstehe, dass man Pointer auf statische Daten erzeugen kann, aber was passiert bei Werten ohne statische Lebensdauer?
    • Es werden nicht die Daten kopiert, sondern Referenzen auf die Daten.
      Die Datenreferenz bleibt über die Lebensdauer des Programms hinweg bestehen. Eine generational box ermöglicht es, Daten aufzunehmen, die kürzer leben als das Programm, aber diese Daten dürfen keine Referenzen enthalten. Wenn die enthaltenen Daten gedroppt werden, wird diese Box für eine andere Allokation wiederverwendet.
      Abgesehen davon, dass Boxen statt einer zentralisierten Arena verwendet werden, ist der Ansatz einer generational Arena sehr ähnlich; das ist eine Entscheidung, um Locking-Probleme zu vermeiden. Wenn man nach dem Drop versucht, über eine Copy-Referenz auf die Daten zuzugreifen, schlägt das mit einer guten Fehlermeldung fehl.
    • Ich kenne diese Crate nicht gut, aber aus Rust-Sicht hat Copy eine bestimmte Bedeutung.
      Wenn ein Typ das Copy-Trait implementiert, heißt das, dass er per memcpy kopiert werden kann; das ist eher eine flache Kopie als eine tiefe Kopie.
      Deshalb verstehe ich es nicht als „Kopieren ohne Kopieren“, sondern so, dass man einen nicht-Copy-Typ wie einen Copy-Typ behandeln kann. In der README steht, dass statische Inhalte erforderlich sind; bei Werten ohne statische Lebensdauer lautet die Antwort also: „Das geht nicht.“
  • Ich beobachte es schon eine Weile und freue mich sehr, dass es veröffentlicht wurde.
    Mir gefällt, dass Dioxus vieles von dem übernimmt, was React erfolgreich gemacht hat, und darauf aufbauend schnell innoviert und ausliefert. Ich freue mich darauf, die Signals dieser Version auszuprobieren.
  • Ich wünschte, es wäre statt RSX etwas, das eher SwiftUI ähnelt als React/JSX.
    Ich erkenne an, was React für JS und das Web Gutes getan hat, und auch seinen Namen, aber wenn man 2024 eine Sprache oder DSL von Grund auf entwerfen kann, glaube ich nicht, dass Code für „reaktive UI“ unbedingt wie React aussehen muss.
    Damit will ich nicht sagen, dass SwiftUI perfekt ist, aber Code in SwiftUI wirkt auf mich viel sauberer organisiert und gekapselt als vergleichbarer Code in React.

Der eigentliche Vorteil von JSX für plattformübergreifende GUIs ist höchstens, dass man bestehende Libraries aus dem Web wiederverwenden kann. Bei RSX scheint der „portierbare Wert“ eher gering zu sein, abgesehen davon, dass Entwickler ihr Konzeptwissen zu JSX auf RSX übertragen können. Da ist es ein besserer Kompromiss, gleich ein neues Paradigma zu lernen, das man objektiv für besser hält als das React/JSX-Paradigma.
Kurz gesagt: Es wäre schön, ein Projekt zu haben, das „SwiftUI, aber plattformübergreifend“ ist, doch das gibt es noch nicht. @Tokamak/TokamakUI kenne ich, aber es wirkt noch sehr unfertig und die Aktivität scheint nachgelassen zu haben.

  • Es gibt https://ribir.org/
    Derzeit werden nur native Desktop-Apps für Linux/mac/windows unterstützt, aber WASM-, Web- und Mobile-Support ist geplant.
  • Ich habe mich für Dioxus entschieden, um die dezentrale Homepage von Freenet zu bauen.
    Sie soll die erste dezentrale Website werden, die Leute sehen, die Freenet eingerichtet haben. Sie ist auch ein wenig ähnlich zu Kweb, einem Kotlin-Web-Framework, an dem ich über die Jahre sporadisch gearbeitet habe – besonders bei der Zustandsverwaltung und der DSL-Art, wie Code auf HTML abgebildet wird. Bisher gefällt es mir.
    https://freenet.org/
    https://kweb.io/
    • Cool. Ich glaube, ich habe mir kweb angesehen, als ich die DSL ursprünglich entworfen habe, und die beiden sind sich wirklich sehr ähnlich.
      Tatsächlich bin ich Kotlin-Fan und mag Kotlin-DSLs sowie das Concurrency-Modell.
  • Ich frage mich, wie es im Vergleich zu Tauri abschneidet.
    • Dazu haben wir etwas im README geschrieben: https://github.com/dioxusLabs/dioxus/?tab=readme-ov-file#dioxus-vs-tauri
      Tauri steckt das Frontend in eine Webview, und mit nativen Rust-Funktionen muss man wie bei Electron über eine IPC-Grenze kommunizieren.
      Bei Dioxus liegt der Rust-Code auf der nativen Seite, daher braucht man für Dinge wie Dateisystemzugriff oder WebSockets kein IPC. Tauri zwingt einen dazu, das Frontend zu WASM zu kompilieren, und viele interessante Rust-Crates lassen sich nicht nach wasm kompilieren.
      Es ist schwer in Worte zu fassen, wie sehr eine fehlende IPC-Grenze die Entwicklung vereinfacht. Da die Dioxus-Tools nur auf Rust abzielen, kommt man in unter einer Minute von null zu einer gebündelten .app. Ein neuer Build dauert etwa 12 Sekunden, ein neues Bundle etwa 20 Sekunden.
      Trotzdem mag ich die Flexibilität von Tauri sehr, nämlich dass man im Frontend alles verwenden kann, was als webkompatible UI funktioniert, und man kann Dioxus auch innerhalb einer Tauri-App nutzen.
    • Genau diese Frage wollte ich auch stellen.
      Es ist gut, dass Dioxus das im README direkt behandelt, aber mich interessieren auch praktische Erfahrungen von Leuten, die beides verwendet haben.
      Ich habe mit Tauri eine Desktop-Spielzeug-App gebaut und geprüft, ob IPC schnell genug ist, dass ein Web-Frontend bei jedem Tastendruck aktualisiert und arbeitet, ohne Verzögerung – die Antwort war „ja“. Ob man bei jedem Tastendruck große Dateien vom Frontend in die Rust-Schicht schicken und wieder zurück ins Frontend bekommen kann, war zumindest bei meiner naiven Implementierung „nein“.
      Sowohl Tauri als auch Dioxus haben viele gute Einsatzfälle, und in manchen davon dürfte Dioxus besser passen. Ich würde gern Vergleichsberichte hören wie „Wir haben uns in unserem Projekt wegen X und Y für Dioxus oder Tauri entschieden“. Dioxus sieht wirklich cool aus, ich freue mich darauf, es auszuprobieren.
  • Ich frage mich, wie native Apps gerendert werden. Läuft das weiterhin in irgendeiner Art Browser-Instanz?
    • Man kann entweder die System-Webview als Renderer verwenden oder eine experimentelle WGPU-basierte Engine wählen, die stylo integriert.
      stylo ist der Teil von Servo, der mit Firefox geteilt wird. Langfristig wollen wir die Leute auf den WGPU-Renderer bringen, aber der ist noch ziemlich früh, und viele Firmen, die Dioxus einsetzen, wissen pragmatisch, dass Webviews für etwa 90 % einer App eine ausreichend gute Lösung sind.
  • Bei der Stelle „Wir planen, während des 0.5-Release-Zyklus die letzten winzigen unsafe-Verwendungen in mehreren Abhängigkeiten zu entfernen“ würde ich gern sehen, wofür diese Verwendungen da sind und was die Motivation ist.
    Ich verstehe, dass Leute manchmal zu leichtfertig unsafe verwenden, aber auch die Standardbibliothek ist voll von unsafe, und die Grenze dort zu ziehen wirkt manchmal wie eine Linie im Sand.
    • Meistens geht es um Interaktion mit FFI oder darum, einige Typen als Send/Sync zu deklarieren.
      Es gibt drei Stellen: ein paar FFI-Korrekturen für iOS, die Implementierung, die Funktionsaufruf-Syntax für signal ermöglicht, und die Implementierung von Send/Sync für eine ID, die einen Pointer als Hash verwendet.
      Letzteres könnte man, wenn ich jetzt darüber nachdenke, vielleicht entfernen, indem man es durch usize ersetzt.
    • Ich stimme zu, dass Leute vor unsafe etwas übermäßig Angst haben können.
      Trotzdem ist es nicht unbedingt eine schlechte Entscheidung, wenn ein Crate-Autor sich zum Ziel setzt, unsafe aus seinem Crate zu entfernen.
      Crate-Autoren, die alles unsafe entfernen wollen, versuchen oft, die Vertrauenslast für Nutzer zu verringern. Das sehe ich als die Stärke des Schlüsselworts unsafe. Eigentlich hätte es vielleicht in trust_me-Blöcke und check_yourself-Funktionen aufgeteilt werden sollen.
      unsafe beschränkt die Diskussion über Speichersicherheit auf sehr enge und auditierbare Stellen im Code und schafft zugleich eine neue, handhabbare Diskussion über Vertrauen.
  • Ich verstehe, warum React für die Hook-API die use*-Namenskonvention eingeführt hat, aber ich frage mich, warum die Funktion zum Erzeugen eines neuen Signals in Dioxus use_signal heißt.
    Ist let mut count = use_signal(|| 0); nicht ein Aufruf, der ein neues Signal erzeugt? Das Signal wird nicht bei jedem Rendern neu erstellt, und genau das ist doch der Kern von Signals.
    In SolidJS erstellt man ein Signal mit createSignal, etwa const [count, setCount] = createSignal(0), und das ist deutlich verständlicher.
    API-Namen sind wichtig, weil State-Hooks anders funktionieren und zusätzliche Anforderungen wie useMemo entstehen können. Ich frage mich, ob es außer dem Wunsch, näher an React zu wirken, einen Grund gab, den Namen use_signal zu wählen.
    • Dioxus rendert Komponenten weiterhin neu, wendet aber viele Optimierungen auf das von einer Komponente erzeugte rsx an und erreicht damit eine Performance, die nahe an Solid liegt.
      Es ist eher näher an Preact als an Solid.
      Zu den Optimierungen gehören das Abtrennen dynamischer UI-Teile in separate „Diff Bins“, standardmäßige Memoization und dass Signals Eigenschaften implizit als dirty/managed markieren.