WebAssembly vs JavaScript: Was ist besserfür SPAs?

Single Page Applications (SPAs) sind heutzutage ein zentrales Architekturkonzept im Web. Anstatt bei jeder Interaktion eine komplette Seite neu zu laden, lädt eine SPA initial einmal die benötigten Ressourcen und aktualisiert anschließend dynamisch die Ansichten im Browser. Diese Technik ermöglicht benutzerfreundliche, schnelle Webanwendungen, stellt jedoch hohe Ansprüche an die Performance der clientseitigen Technologie. Traditionell werden SPAs mit JavaScript und darauf basierenden Frameworks entwickelt. Mit WebAssembly (WASM) steht seit einigen Jahren eine Alternative zur Verfügung, die native Code-Ausführung im Browser verspricht. Dadurch können Entwickler z.B. in Sprachen wie C++ oder C# Webanwendungen schreiben, die dann in WebAssembly kompiliert werden und im Browser laufen.

Warum ist ein Vergleich von JavaScript und WebAssembly für SPAs relevant? Zum einen hat JavaScript sich über Jahrzehnte als De-facto-Standard für Webentwicklung etabliert. Zum anderen bietet WebAssembly theoretisch Performance-Vorteile und die Möglichkeit, existierenden nativen Code wiederzuverwenden. In der Praxis stellt sich jedoch die Frage, ob eine komplette SPA in WebAssembly tatsächlich schneller oder effizienter ist als eine klassische JavaScript-Implementierung. In diesem Blogartikel werden die beiden Technologien sachlich gegenübergestellt. Anhand zweier Beispielprojekte – einer in JavaScript und einer in WebAssembly – werden Performance und Funktionalität verglichen, um praktische Erkenntnisse für Softwareentwickler zu liefern. Die Analyse basiert auf einer experimentellen Untersuchung (im Rahmen einer Bachelorarbeit), sodass die Ergebnisse aus realen Testanwendungen stammen und weniger theoretisch, sondern vor allem praxisnah sind.

Technische Grundlagen

JavaScript ist eine interpretierte bzw. JIT-kompilierte Skriptsprache, die seit Mitte der 1990er Jahre in allen gängigen Webbrowsern läuft. JavaScript-Code wird vom Browser zur Laufzeit gelesen, von der JavaScript-Engine (z.B. V8 in Chrome oder SpiderMonkey in Firefox) in Maschinencode übersetzt und ausgeführt. Moderne JavaScript-Engines optimieren den Code während der Ausführung, um eine möglichst hohe Geschwindigkeit zu erreichen. Da JavaScript ursprünglich entwickelt wurde, um Webseiten interaktiv zu machen, hat es vollen Zugriff auf das DOM (Document Object Model) und die Browser-APIs. Das bedeutet, JavaScript kann direkt HTML-Elemente erzeugen, ändern und auf Benutzereingaben reagieren. Im Kontext von SPAs ermöglicht JavaScript in Kombination mit Frameworks wie Vue.js, React oder Angular eine komponentenbasierte Entwicklung, Routing innerhalb der Anwendung und asynchrone Datenkommunikation per AJAX.

WebAssembly (WASM) hingegen ist kein eigenständiges Framework, sondern ein binäres Bytecode-Format für eine virtuelle Maschine im Browser. Anders als JavaScript wird WebAssembly-Code vorab aus einer Hochsprache (z.B. C, C++ oder Rust, aber auch C# über Blazor) kompiliert. Der Browser kann diesen Bytecode dann sehr schnell laden und nahezu in nativer Geschwindigkeit ausführen, ohne ihn jedes Mal neu interpretieren zu müssen (WebAssembly: Solving Performance Problems on the Web — SitePoint). WebAssembly wurde entwickelt, um Performance-Probleme zu lösen und rechenintensive Aufgaben im Web zu beschleunigen (WebAssembly: Solving Performance Problems on the Web — SitePoint) (WebAssembly: Solving Performance Problems on the Web — SitePoint). In der Theorie erreicht WebAssembly daher für CPU-lastige Berechnungen eine ähnlich hohe Geschwindigkeit wie native Anwendungen. Allerdings hat WebAssembly derzeit auch Einschränkungen: Es besitzt keinen direkten Zugriff auf das DOM oder Browser-spezifische APIs und muss für solche Interaktionen auf JavaScript zurückgreifen. Auch DOM Manipulationen müssen immer über einen spezifischen JavaScript Glue Code erfolgen, was einen zusätzlichen Overhead bringt. WebAssembly-Module laufen in einer sicheren Sandbox-Umgebung und kommunizieren über eine Schnittstelle mit JavaScript, wenn z.B. Elemente in der Benutzeroberfläche geändert werden sollen. Seit der Veröffentlichung des ersten MVP (Minimum Viable Product) von WebAssembly Ende 2019 hat sich der Funktionsumfang erweitert, jedoch steht die Technologie im Vergleich zu JavaScript noch am Anfang. Entwickler können WebAssembly heutzutage unter anderem über Frameworks wie Blazor (für .NET/C#) oder mittels Toolchains wie Emscripten (für C/C++) bzw. wasm-bindgen (für Rust) in Webprojekte einbinden. Für unseren Vergleich ist besonders Blazor interessant, da es ermöglicht, eine komplette SPA in C# zu schreiben, die dann als WebAssembly im Browser läuft.

Umsetzung der Testprojekte

Um JavaScript und WebAssembly unter realistischen Bedingungen zu vergleichen, wurden zwei inhaltlich identische Single Page Applications implementiert: eine mit JavaScript und eine mit WebAssembly. Für die JavaScript-Variante kam das Framework Vue.js zum Einsatz, während die WebAssembly-Variante mit Blazor in C# entwickelt wurde. Beide SPAs wurden so konzipiert, dass sie dieselben Funktionen bieten und damit vergleichbar sind. Die Kernanforderungen an beide Testanwendungen waren:

  • Video-Wiedergabe: Einbetten und Abspielen eines Videos innerhalb der Anwendung.
  • Bildergalerie: Laden und Anzeigen von Bildern.
  • Routing: Navigationsmechanismus, um zwischen verschiedenen Ansichtsseiten (Views) wechseln zu können, ohne die Seite neu zu laden.
  • Datenanzeige: Rendern einer Tabelle mit 1.000 Einträgen (Zeilen) zur Demonstration der Fähigkeit, auch größere Datenmengen in der UI darzustellen.
  • API-Kommunikation: Abruf von Daten über eine JSON-HTTP-API und Darstellung der erhaltenen Inhalte in der Anwendung.

Zusätzlich zu diesen funktionalen Anforderungen wurde in beiden Anwendungen ein Performance-Test integriert. Dazu wurde ein Sortieralgorithmus (Quicksort) implementiert, um die Rechenleistung der beiden Technologien direkt zu messen. Die Idee dahinter war, ein rechenintensives Szenario zu schaffen, das weniger von Netzwerk oder DOM abhängt, sondern primär die Geschwindigkeit der Ausführung im Browser widerspiegelt. Beide Versionen – JavaScript und C# (WASM) – sortieren hierbei Listen von Zahlen und Zeichenfolgen verschiedener Größen (bis zu 100.000 Elemente) und messen die benötigte Zeit.

Für faire Bedingungen wurden beide Anwendungen im Produktivmodus (minifizierter/optimierter Build) erstellt und auf demselben Webserver gehostet. Die Tests fanden in aktuellen Browsern (Chrome, Firefox) auf dem Desktop sowie auf einem modernen Smartphone (Safari in iOS) statt, um sowohl Desktop- als auch mobile Performance zu betrachten. Auf diese Weise konnte ermittelt werden, wie sich eine JavaScript-basierte SPA gegenüber der WebAssembly-basierten SPA in praxisnahen Situationen verhält, sowohl in Bezug auf Lade- und Renderzeiten als auch hinsichtlich der Laufzeit-Performance bei der Ausführung von Logik.

Ergebnisse

Funktionsfähigkeit: Beide Technologien erwiesen sich als grundsätzlich geeignet, die gestellten Anforderungen umzusetzen. Sowohl die JavaScript-Variante (Vue.js) als auch die WebAssembly-Variante (Blazor) konnten alle vorgesehenen Features implementieren. Videos und Bilder wurden in beiden Apps erfolgreich geladen und dargestellt, das Routing funktionierte reibungslos, und die Tabellen mit 1.000 Einträgen wurden korrekt gerendert. Auch die Kommunikation mit der externen JSON-API verlief in beiden Fällen ohne Probleme, sodass Daten abgerufen und angezeigt werden konnten. In Bezug auf Browser-Kompatibilität zeigte sich, dass die JavaScript-App erwartungsgemäß in allen gängigen Browsern (einschließlich älterer wie Internet Explorer) lauffähig war. Die WebAssembly-App lief in allen modernen Browsern (Chrome, Firefox, Edge, Safari) sowohl auf dem Desktop als auch mobil stabil; lediglich in Internet Explorer wurde sie mangels WASM-Unterstützung nicht ausgeführt. Damit ist festzuhalten, dass aus funktionaler Sicht beide Ansätze praxistauglich sind – es gab keine Feature-Anforderungen, die mit WebAssembly gar nicht umsetzbar gewesen wären. Unterschiede zeigten sich jedoch deutlich bei den Performance-Messungen.

Performance (Ladezeit und Anzeige): Die JavaScript-SPA erzielte in den Performance-Tests signifikant bessere Ergebnisse als die WebAssembly-SPA. Insbesondere die automatisierten Bewertungen mit Google Lighthouse – einem Tool, das die Ladegeschwindigkeit und Responsiveness einer Webanwendung mit einem Score von 0 bis 100 bewertet – fielen zugunsten von JavaScript aus. In sämtlichen getesteten Szenarien erreichte die Vue.js-Anwendung einen weit höheren Performance-Score als die Blazor-Anwendung. Konkret lag der Lighthouse-Score der WebAssembly-App oft weniger als halb so hoch wie der der JavaScript-App. Beispielsweise erzielte die JavaScript-Version für die Startseite einen Wert im hohen grünen Bereich (nahe an 100 Punkten), während die WebAssembly-Version nur etwa 40–50 Punkte erreichte. Dieser Trend zeigte sich konsistent über verschiedene Unterseiten der Anwendungen und insbesondere im mobilen Kontext: Auf einem Smartphone brach die Performance der Blazor-Anwendung deutlich ein, wohingegen die JavaScript-App weiterhin flüssig lud.

(Lighthouse Score im Vergleich der verschiedenen Seiten der Anwendung)

Der Grund für die schwächere Performance der WebAssembly-Lösung bei den Ladezeiten liegt vor allem im initialen Overhead. Die in WebAssembly kompilierte Blazor-App musste zunächst eine relativ große Menge an Daten laden – inklusive der .NET-Laufzeit und der Anwendung selbst. Das Download-Paket der Blazor-SPA betrug in der Testkonfiguration über 8 MB, wohingegen die fertig gebündelte JavaScript-App (Vue.js) nur ca. 130 KB umfasste. Dieses extreme Größenungleichgewicht erklärte die verlängerte Ladezeit der WASM-Variante: Bis sämtliche Ressourcen geladen und die Anwendung im Browser initialisiert war, vergingen mehrere Sekunden, während die JavaScript-App nahezu sofort startklar war. Metriken wie der Largest Contentful Paint (Zeitpunkt, zu dem das größte Inhaltselement sichtbar wird) traten bei WebAssembly deutlich später auf als bei JavaScript, was den niedrigeren Lighthouse-Score zur Folge hatte. Bemerkenswert ist, dass die Ladezeit der Blazor-App relativ konstant langsam war – auf unterschiedlichen Geräten ergaben sich ähnlich hohe Zeiten –, während die JavaScript-App teils variablere, aber insgesamt viel kürzere Ladezeiten aufwies. Unterm Strich bedeutet dies: Eine JavaScript-basierte SPA kann den Nutzer wesentlich schneller mit einer interaktiven Oberfläche bedienen als die entsprechende WebAssembly-Variante, was für die User Experience gerade auf mobilen Geräten kritisch ist.

(Vergleich der Paketgrößen von Vue.js und Blazor)

Performance (Rechenleistung): Neben den Ladezeiten wurde die Laufzeit-Performance mittels des implementierten Quicksort-Tests verglichen. Hier war die Erwartung, dass WebAssembly aufgrund der Nähe zum nativen Maschinencode einen Vorteil gegenüber JavaScript haben könnte – schließlich wurde die Sortierlogik in C# geschrieben und kompiliert. Die Messergebnisse zeigten jedoch das Gegenteil: Die JavaScript-Version sortierte die Daten erheblich schneller als die WebAssembly-Version. Je größer die zu sortierende Liste, desto deutlicher wurde der Abstand. Ein Beispiel: Das Sortieren von 100.000 String-Elementen dauerte in der Blazor-Anwendung (WASM) in Google Chrome im Durchschnitt rund 68,6 Sekunden, während die gleiche Operation in der Vue.js-Anwendung (JavaScript) nur ca. 0,066 Sekunden benötigte – die WebAssembly-Variante brauchte hier also etwa 1000-fach länger (Bacherlorarbeit.pdf). Ähnlich eklatante Unterschiede zeigten sich in Firefox und auf einem iPhone (Safari), wo die JavaScript-Implementierung die Sortierung jeweils in einem Bruchteil der Zeit abschloss, die die WebAssembly-Implementierung benötigte.

(Zeitdiagramm für Sortierung mit Zahlen)

Diese Ergebnisse waren überraschend, da WebAssembly eigentlich auf hohe Ausführungsgeschwindigkeit abzielt. Zur Erklärung wurde ein zusätzlicher Test durchgeführt: Die gleiche Sortierfunktion in C# wurde als native Konsolenanwendung (ohne WebAssembly, direkt auf dem PC) ausgeführt. Dabei zeigte sich, dass der native C#-Code tatsächlich sehr performant war – teils sogar schneller als das JavaScript-Pendant. Der Flaschenhals liegt also nicht in der Sprache C# selbst, sondern in der Art und Weise, wie die C#-Logik innerhalb der WebAssembly-Umgebung abläuft. Im Fall von Blazor wird der .NET-Code zur Laufzeit in WebAssembly interpretiert/ausgeführt, was offensichtlich mit einem erheblichen Overhead verbunden ist. Zudem kostet die Übergabe von Daten zwischen dem WebAssembly-Modul und dem JavaScript-Kontext (z.B. für die Darstellung von Ergebnissen im Browser) weitere Zeit. In unserem Test wurde deutlich, dass JavaScript bei rechenintensiven Aufgaben derzeit effektiver ist, sofern es sich um im Browser ausgeführten Code handelt. Selbst rechenlastige Sortieroperationen meisterte die optimierte JS-Engine schneller als die WebAssembly-Alternative, vermutlich dank Just-in-Time-Optimierungen und direktem Zugriff auf die Datenstrukturen im Speicher ohne Zwischenschicht.

Zusammenfassend lässt sich zu den Ergebnissen sagen: Aus Performance-Sicht hat die JavaScript-SPA in allen relevanten Kategorien besser abgeschnitten. Sie lädt schneller, reagiert schneller und bewältigt algorithmische Aufgaben in kürzerer Zeit als die WebAssembly-SPA. Die WebAssembly-Anwendung konnte zwar funktional mithalten, war aber hinsichtlich Benutzererlebnis (Wartezeiten) deutlich im Nachteil. Lediglich in Bezug auf die Stabilität und Umsetzbarkeit der Funktionen gab es keine nennenswerten Unterschiede – hier waren beide Ansätze erfolgreich.

Fazit

Die Gegenüberstellung von JavaScript und WebAssembly für die Entwicklung von Single Page Applications zeigt ein klares Bild: Grundsätzlich ist es bereits heute möglich, eine vollständige SPA allein mit WebAssembly zu entwickeln, wie das Blazor-Beispiel belegt. Alle typischen SPA-Funktionen konnten umgesetzt werden, und moderne Browser unterstützen WebAssembly ausreichend, um solche Anwendungen auszuführen. Allerdings bringt dieser Ansatz in der Praxis derzeit keine Vorteile, sondern vielmehr deutliche Nachteile gegenüber der klassischen JavaScript-Entwicklung. In den durchgeführten Performance-Tests war die WebAssembly-Lösung in allen Belangen langsamer – von der Ladezeit der Anwendung bis zur Ausführung komplexerer Berechnungen. Insbesondere die Startzeit einer WebAssembly-basierten SPA ist wegen der großen zu ladenden Datenmenge erheblich höher, was die Nutzererfahrung beeinträchtigt. Auch die theoretisch mögliche Rechengeschwindigkeit von WebAssembly konnte in diesem Szenario nicht ausgespielt werden, da der Overhead der Laufzeitumgebung die Vorteile zunichtemachte.

Auf Basis dieser Ergebnisse ist es nicht ratsam, zum jetzigen Zeitpunkt eine komplette Single Page Application mit WebAssembly (anstelle von JavaScript) umzusetzen. Entwickler fahren in den meisten Fällen besser damit, auf bewährte JavaScript-Frameworks zu setzen, die eine ausgereifte Performance und breite Browser-Kompatibilität bieten. WebAssembly eignet sich momentan eher als Ergänzung – etwa um spezifische, sehr rechenintensive Module innerhalb einer Webanwendung zu beschleunigen –, aber nicht als Ersatz für JavaScript im Frontend. Natürlich befindet sich WebAssembly weiterhin in Entwicklung: Künftige Verbesserungen (z.B. optimierte Compiler, direkte DOM-APIs für WASM oder geringerer Overhead) könnten die Situation verändern. Zum jetzigen Stand aber bleibt JavaScript für die Entwicklung von SPAs die praktischere und performantere Wahl. Die durchgeführte Analyse untermauert damit die gängige Einschätzung, dass WebAssembly zwar ein enormes Potenzial besitzt, dieses in typischen Web-Anwendungen aber erst in den kommenden Jahren voll zur Geltung bringen wird – während JavaScript seinen Platz als Haupttechnologie im Browser vorerst behauptet.