Bereitstellung von Emscripten-kompilierten Seiten

Die kompilierten Ausgaben von Emscripten können entweder direkt in einer JS-Shell über die Befehlszeile ausgeführt oder auf einer Webseite gehostet werden. Beim Hosting von asm.js- und WebAssembly-kompilierten Seiten als .html zur Ausführung in Browsern stellt Emscripten eine standardmäßige HTML-Shell-Datei bereit, die als Launcher zum Ausführen des Codes dient und den Einstieg in die Entwicklung vereinfacht. Wenn Sie jedoch bereit für die Veröffentlichung und das Hosting der Inhalte auf einer Website sind, werden wahrscheinlich eine Reihe zusätzlicher Funktionen und Anpassungen benötigt, um das Besuchererlebnis zu verfeinern. Dieser Leitfaden hebt Dinge hervor, auf die man bei der Bereitstellung von Websites für die Öffentlichkeit achten sollte.

Build-Dateien und benutzerdefinierte Shell

Die Build-Ausgabe von Emscripten besteht aus zwei wesentlichen Teilen: 1) dem kompilierten Low-Level-Codemodul und 2) der JavaScript-Laufzeitumgebung für die Interaktion mit diesem Modul. Wenn Sie beispielsweise mit -o out.html bauen, wird der kompilierte Code in einer Datei out.wasm gespeichert und die Laufzeitumgebung befindet sich in einer Datei out.js. Wenn das Ziel asm.js ist, existiert eine zusätzliche Binärdatei out.mem, die den statischen Speicherabschnitt des kompilierten Codes enthält. Dieser Teil ist bei der Ausrichtung auf WebAssembly in der Datei out.wasm eingebettet.

Zusätzliche Build-Ausgabedateien können ebenfalls existieren, je nachdem, welche Funktionen verwendet werden. Wenn der Emscripten-Dateipacker verwendet wird, wird ein binäres out.data-Paket zusammen mit einer zugehörigen Laderdatei out.data.js generiert. Auch die Emscripten-pthreads- und Fetch-APIs haben ihre eigenen zugehörigen Web-Worker-Skript-Ausgabedateien (.js).

Entwickler können wählen, ob die Ausgabe in JavaScript oder HTML erfolgen soll. Bei der Ausgabe in JavaScript (emcc -o out.js) wird erwartet, dass der Entwickler die Hauptseite out.html, in der der Code in Browsern ausgeführt wird, manuell erstellt. Wenn HTML das Ziel ist (emcc -o out.html, der empfohlene Build-Modus), generiert Emscripten die HTML-Shell-Datei automatisch. Diese Shell-Datei kann mit der Linker-Direktive emcc -o out.html --shell-file path/to/custom_shell.html angepasst werden. Kopieren Sie die standardmäßige minimale HTML-Shell-Datei aus dem Emscripten-Repository in Ihren Projektbaum, um eine gute Ausgangsvorlage für eine angepasste Shell-Datei zu erhalten.

Die folgenden Abschnitte bieten Tipps zur Verbesserung des Website-Erlebnisses.

Optimierung der Download-Größen

Die größte Verlangsamung beim schnellen Laden der Seite ist meist die Notwendigkeit, große Mengen an Asset-Daten für das Projekt herunterzuladen, insbesondere wenn die Seite viele WebGL-Texturen oder Geometrien enthält. Kompilierter Code nimmt im Allgemeinen mehr Platz ein als handgeschriebenes JavaScript, aber Maschinencode lässt sich effizient komprimieren. Daher ist es beim Hosting von asm.js und WebAssembly entscheidend sicherzustellen, dass alle Inhalte mit gzip-Komprimierung übertragen werden, die heutzutage alle Browser und CDNs nativ unterstützen. Die gzip-Komprimierung von .wasm-Dateien führt im Durchschnitt zu einer Größenreduzierung von 60-75% im Vergleich zu unkomprimierten Dateien, sodass es praktisch nie sinnvoll ist, unkomprimierte Dateien auszuliefern.

  • Um gzip-komprimierte Assets auf einem CDN bereitzustellen, verwenden Sie ein gzip-Komprimierungstool und komprimieren Sie die Asset-Dateien offline vor dem Hochladen auf das CDN. Einige Webserver unterstützen die Komprimierung von Dateien während des Betriebs, dies sollte jedoch für statische Asset-Inhalte vermieden werden, da es die Server-CPU belasten kann, die Dateien ständig neu zu komprimieren. Passen Sie die Konfiguration des Webservers so ab, dass die vorkomprimierten Dateien mit dem HTTP-Response-Header Content-Encoding: gzip ausgeliefert werden. Dies weist Webbrowser an, dass der heruntergeladene Inhalt transparent dekomprimiert werden sollte, bevor die Daten an die Seite selbst übergeben werden.

  • Stellen Sie sicher, dass die gzip-Komprimierung nicht die MIME-Typen verfälscht, mit denen die Assets ausgeliefert werden. Alle JavaScript-Dateien (vorkomprimiert oder nicht) sollten am besten mit dem HTTP-Response-Header Content-Type: application/javascript ausgeliefert werden, und alle Asset-Dateien (.data, .mem) mit dem Header Content-Type: application/octet-stream. WebAssembly-.wasm-Dateien sollten mit Content-Type: application/wasm ausgeliefert werden.

  • Versuchen Sie, die Menge der vorab geladenen Asset-Daten zu minimieren, die mit dem Emscripten-Linker-Flag --preload-file vorab heruntergeladen werden. Diese Datendatei wird geladen, bevor die von Emscripten kompilierte Anwendung überhaupt mit der Ausführung der main()-Funktion beginnt. Daher können alle in diesem Paket gespeicherten Dateien die Startzeit erheblich verlangsamen. Es ist vorzuziehen, heruntergeladene Asset-Dateien in mehrere separate Pakete aufzuteilen und asynchrone Asset-Download-APIs in Emscripten zu verwenden, die während der Laufzeit der Anwendung arbeiten können.

  • Asset-Größen für WebGL-Anwendungen werden oft durch die Menge an Texturen dominiert, sodass die Verwendung komprimierter Texturformate hilft, die Asset-Größen zu schrumpfen. Das Web kann eine ganz andere Zielplattform als native Plattformen sein, da man im Web nicht unbedingt davon ausgehen kann, dass ein bestimmtes komprimiertes Texturformat von der Hardware des Besuchers unterstützt wird, insbesondere wenn man eine Website entwickelt, die sowohl auf Mobil- als auch auf Desktop-Browsern funktionieren soll. Die bewährte Methode zur Unterstützung einer breiten Palette von Hardware besteht darin, mehrere Sätze komprimierter Texturen zu generieren (einen für jede unterstützte Plattform) und den entsprechenden Satz basierend auf den vom WebGL-Kontext unterstützten Formaten herunterzuladen.

  • Wenn mehrere Bildschirmgrößen angepeilt werden, z. B. Desktop- und Mobil-Formfaktoren, sollten Sie in Betracht ziehen, Texturen in SD- und HD-Varianten zu trennen, damit die Seite für mobile Geräte mit einer geringeren Displayauflösung schneller geladen wird.

Optimierung der Seitenstartzeiten

Zusätzlich zum Herunterladen der Seite können auch andere Teile der Startsequenz manchmal langsam sein. Zu berücksichtigende Punkte sind hierbei:

  • Wenn asm.js das Ziel ist und die Ausführung in Firefox oder Edge erfolgt, zeigt die Konsole der Webseite eine Protokollmeldung an, nachdem das asm.js-Modul kompiliert wurde. Diese Nachricht enthält Zeitinformationen darüber, wie lange die Kompilierung gedauert hat. Die asm.js-Kompilierung beginnt in dem Moment, in dem die asm.js-Skript-Quelldatei zum DOM hinzugefügt wird. Sobald sie abgeschlossen ist, wird das onload-Ereignis des Skript-Tags aufgerufen. Dies kann verwendet werden, um zu messen, wie lange die asm.js-Kompilierung in Safari, Opera und Chrome dauert.

  • Es wird empfohlen, auf WebAssembly zu migrieren, um die Startzeiten von kompiliertem Code in Browsern zu beschleunigen. WebAssembly-Module lassen sich im Vergleich zu asm.js viel schneller parsen und kompilieren. Zusätzlich können kompilierte WebAssembly.Module-Objekte manuell in IndexedDB persistent gespeichert werden, wodurch der Kompilierungsschritt beim zweiten Durchlauf gänzlich entfällt. (siehe nächster Abschnitt)

  • Gelegentlich kann es passieren, dass ein langsamer Start fälschlicherweise der asm.js/WebAssembly-Kompilierung zugeschrieben wird, obwohl die eigentliche Ursache der Langsamkeit in der Ausführung des main()-Funktionseinstiegspunkts der Anwendung selbst liegt. Dies liegt daran, dass diese beiden Aktionen zeitlich sehr nah beieinander liegen. Es lohnt sich, präzise zu sein und diese beiden Aktionen separat zu profilieren; sehen Sie sich die function callMain() in src/preamble.js an, die die Ausführung des Anwendungs-main()-Codes startet. Wenn die Ausführung von main() zu lange dauert, sollten Sie in Erwägung ziehen, sie in separate Operationen aufzuteilen, die durch mehrere setTimeout()-Aufrufe oder durch die emscripten_set_main_loop()-Ereignisschleife gesteuert werden.

  • Um Netzwerkübertragungen zu beschleunigen, zeigt die Erfahrung, dass unter regulären Netzwerkbedingungen der schnellste Ansatz darin besteht, alle Netzwerk-Downloads aggressiv gleichzeitig parallel zu starten (vorausgesetzt, es sind nur eine Handvoll), im Gegensatz zum Herunterladen einzelner Dateien nacheinander. Versuchen Sie daher zur Maximierung der Netzwerkübertragungsgeschwindigkeit, die HTML-Hauptseite der Anwendung so zu schreiben, dass alle benötigten Netzwerk-Downloads parallel gestartet werden, anstatt sie für eine sequentielle Übertragung in eine Warteschlange zu stellen.

  • In Fällen, in denen das erste Laden der Seite durch Netzwerkübertragungen dominiert wird, ist es nützlich, die Tatsache zu nutzen, dass die CPU ansonsten meist untätig ist, während sie auf den Abschluss der Downloads wartet. Diese CPU-Zeit kann genutzt werden, um andere schwere Aufgaben auszuführen. Ein idealer Kandidat dafür ist das Herunterladen und Kompilieren des asm.js/WebAssembly-Moduls bereits während des Downloads anderer Seiten-Assets.

  • Ein derzeit bekanntes Problem auf Windows-basierten Systemen ist, dass das Kompilieren von WebGL-Shadern langsam sein kann. Dies ist ebenfalls ein Hauptkandidat für eine Aktion, die parallel zum Herunterladen anderer Assets für die Seite durchgeführt werden sollte.

Einen schnellen Zweitstart ermöglichen

Während der erste Besuch einer Seite einige Zeit in Anspruch nehmen kann, um alle Downloads abzuschließen, kann der zweite Besuch viel schneller gestaltet werden, indem sichergestellt wird, dass die Ergebnisse des ersten Besuchs vom Browser zwischengespeichert werden.

  • Alle Browser haben ein implementationsdefiniertes Limit (20 MB oder 50 MB) für Assets; Dateien, die größer als dieses Limit sind, umgehen die integrierten Web-Caches des Browsers gänzlich. Daher wird empfohlen, große .data-Dateien manuell von der Hauptseite in IndexedDB zwischenzuspeichern. Die Emscripten-Linker-Option --use-preload-cache kann verwendet werden, um dies von Emscripten implementieren zu lassen, obwohl es wünschenswert sein kann, dies auf der HTML-Seite manuell zu verwalten, da dies die Kontrolle darüber ermöglicht, in welcher Datenbank die Assets zwischengespeichert werden und welches Schema zum Löschen von Daten daraus verwendet wird.

  • Während .wasm-Dateien wie jede Ressource automatisch zwischengespeichert werden, müssen sie dennoch im Browser kompiliert werden, bevor sie instanziiert werden können. Glücklicherweise unterstützen Chromium-basierte Browser das automatische Caching von kompilierten WebAssembly-Modulen (lesen Sie diesen v8.dev Blogpost). Früher wurde das manuelle Caching von kompilierten WebAssembly-Modulen über IndexedDB empfohlen; dies wird jedoch mittlerweile weitgehend nicht mehr unterstützt (Details in diesem WebAssembly-Spec-Ticket).

  • Wenn der kompilierte C/C++-Code selbst Berechnungen durchführt, z. B. in main(), die beim zweiten Laden übersprungen werden könnten, verwenden Sie entweder IndexedDB oder die localStorage-APIs, um die Ergebnisse dieser Berechnung über Seitenaufrufe hinweg zwischenzuspeichern. IndexedDB eignet sich zum Speichern großer Dateien, arbeitet jedoch asynchron. Die localStorage-API hingegen ist vollständig synchron, ihre Verwendung ist jedoch auf das Speichern kleiner Datenfelder im Cookie-Stil beschränkt.

  • Bei der Implementierung von IndexedDB-basiertem Caching ist zu beachten, dass IndexedDB-Operationen als asynchrone API, die auf die Festplatte zugreift, ebenfalls eine gewisse Latenz aufweisen. Wenn Sie beim Start mehrere Leseoperationen durchführen, ist es daher ratsam, diese nach Möglichkeit parallel zu starten, um die Latenz zu verringern.

  • Ein weiterer wichtiger Punkt bei der dauerhaften Speicherung von Daten ist, dass es im Sinne bewährter Methoden gegenüber dem Benutzer gut ist, eine explizite visuelle Kennzeichnung bereitzustellen, wenn IndexedDB oder localStorage zur Speicherung großer Datenmengen verwendet wird, und einen einfachen Mechanismus zum Löschen oder Deinstallieren dieser Daten anzubieten. Dies liegt daran, dass Browser derzeit keine komfortablen Benutzeroberflächen für das feingranulare Löschen von Daten aus diesen Speichern implementieren, sondern das Löschen von Daten oft als Option vom Typ „Cache aller Seiten löschen“ präsentiert wird.

Speicherreservierung für kompilierten Code

Eine inhärente Eigenschaft von asm.js- und WebAssembly-Anwendungen ist, dass sie einen linearen Speicherblock benötigen, um den Heap der Anwendung darzustellen. Dies ist oft die größte einzelne Speicherallokation, die eine Emscripten-kompilierte Seite vornimmt, und daher diejenige, bei der das größte Risiko besteht, dass sie fehlschlägt, wenn das System des Benutzers über wenig Speicher verfügt.

Da diese Speicherallokation zusammenhängend sein muss, kann es vorkommen, dass der Browserprozess des Benutzers zwar über genügend Speicher verfügt, aber der Adressraum des Prozesses zu stark fragmentiert ist und nicht genügend linearer Adressraum verfügbar ist, um die Allokation zu erfüllen. Um dieses Problem zu vermeiden, besteht die bewährte Methode darin, das WebAssembly.Memory-Objekt (ArrayBuffer für asm.js) vorab ganz oben auf der Hauptseite zu allozieren, bevor andere Allokationen oder Skript-Ladeaktionen der Seite erfolgen. Dies stellt sicher, dass die Allokation die besten Erfolgschancen hat. Weitere Informationen finden Sie in den Feldern Module['buffer'] und Module['wasmMemory'].

Zusätzlich ist es möglich, sich gezielt für eine Isolierung des Inhaltsprozesses für eine Webseite zu entscheiden, die eine derart große Allokation benötigt. Um diesen Mechanismus zu nutzen, geben Sie den HTTP-Response-Header Large-Allocation: <MBytes> an, wenn Sie die Haupt-HTML-Seite ausliefern. Diese Unterstützung ist derzeit in Firefox 53 implementiert.

Schließlich ist es leicht, versehentlich an großen, nicht mehr benötigten Speicherblöcken festzuhalten, nachdem die Seite geladen wurde. In WebAssembly zum Beispiel wird das ursprüngliche WebAssembly.Module-Objekt nicht mehr im Speicher benötigt, sobald es zu einem WebAssembly.Instance-Objekt instanziiert wurde. Es ist am besten, alle Referenzen darauf zu löschen, damit der Garbage Collector es zurückgewinnen kann, da das Modul-Objekt Dutzende Megabyte groß sein kann. Stellen Sie auf ähnliche Weise sicher, dass auf alle per XHR geladenen Dateien, Asset-Daten und großen Skripte nicht mehr verwiesen wird, wenn sie nicht mehr verwendet werden. Nutzen Sie das Speicher-Profiling-Tool des Browsers und die Seite about:memory in Firefox, um sicherzustellen, dass kein Speicher verschwendet wird.

Robuste Fehlerbehandlung

Um das bestmögliche Benutzererlebnis zu bieten, stellen Sie sicher, dass die verschiedenen Arten, wie die Seite fehlschlagen kann, berücksichtigt werden und eine gute Fehlermeldung für den Benutzer bereitgestellt wird. Gehen Sie insbesondere die folgende Checkliste für Best Practices durch.

  • Versuchen Sie, so früh wie möglich zu scheitern. Eine große Frustrationsquelle für Benutzer sind Szenarien, in denen das System des Benutzers nicht bereit ist, die angegebene Seite auszuführen, der Fehler aber erst offensichtlich wird, nachdem eine Minute gewartet wurde, um Assets im Wert von 100 MB herunterzuladen. Versuchen Sie beispielsweise, den benötigten Heap-Speicher vorab zu allozieren, bevor Sie die Seite tatsächlich laden. Auf diese Weise ist der Fehler sofort erkennbar, wenn die Speicherallokation fehlschlägt, und es müssen gar keine Asset-Downloads versucht werden.

  • Wenn bekannt ist, dass ein bestimmter Browser nicht unterstützt wird, widerstehen Sie der Versuchung, das Feld navigator.userAgent auszulesen, um Benutzer mit diesem Browser auszuschließen, falls dies irgendwie möglich ist. Wenn Ihre Seite beispielsweise WebGL 2 benötigt, aber bekannt ist, dass Safari dies nicht unterstützt, schließen Sie Safari-Benutzer nicht mit einer Prüfung des folgenden Typs aus:

    if (navigator.userAgent.indexOf('Safari') != -1) alert('Your browser does not support WebGL 2!');
    

sondern erkennen Sie stattdessen die tatsächlichen Fehler:

if (!canvas.getContext('webgl2')) alert('Your browser does not support WebGL 2!'); // And look for webglcontextcreationerror here for an error reason.

Auf diese Weise wird die Seite zukunftskompatibel sein, sobald die Unterstützung für das jeweilige Feature später verfügbar wird.

  • Testen Sie verschiedene Fehlerfälle vorab, indem Sie unterschiedliche Probleme und Browser-Einschränkungen simulieren. In Firefox ist es beispielsweise möglich, WebGL 2 manuell zu deaktivieren, indem Sie zu about:config navigieren und die Einstellung webgl.enable-webgl2 auf false setzen. Dies ermöglicht es Ihnen zu debuggen, welche Art von Fehlermeldung Ihre Seite dem Benutzer in einem solchen Szenario präsentiert. Um die WebGL-Unterstützung zu Testzwecken gänzlich zu deaktivieren, setzen Sie die Einstellung webgl.disabled auf true.

  • Bereiten Sie sich bei der Arbeit mit IndexedDB darauf vor, "Out-of-Quota"-Fehler zu behandeln, wenn dem Benutzer der freie Festplattenspeicher oder das erlaubte Kontingent für die Domain ausgeht.

  • Simulieren Sie "Out-of-Memory"-Fehler, indem Sie unrealistisch viel Speicher für das WebAssembly.Memory-Objekt und für die vorab geladenen Dateipakete (falls verwendet) allozieren. Stellen Sie sicher, dass Speichermangel-Fehler korrekt als solche gekennzeichnet (und dem Benutzer oder einer Fehlerdatenbank gemeldet) werden.

  • Simulieren Sie Download-Timeouts entweder invasiv durch programmatisches Abbrechen von XHR-Downloads, durch physisches Trennen des Netzwerkzugangs oder durch Verwendung externer Tools wie Fiddler. Diese Arten von Tools können viele unerwartete Fehlerfälle aufzeigen und helfen zu diagnostizieren, ob der Fehlerbehandlungspfad für solche Szenarien wie gewünscht funktioniert.

  • Verwenden Sie ein Netzwerk-Limiter-Tool, um die Download- oder Upload-Bandbreite einzuschränken, um langsame Netzwerkverbindungen zu simulieren. Dies kann Fehler im Zusammenhang mit Zeitabhängigkeiten bei Netzwerkübertragungen aufdecken. Beispielsweise könnte implizit angenommen werden, dass eine kleine Netzwerkübertragung vor einer großen abgeschlossen ist, was jedoch nicht immer der Fall sein muss.

  • Führen Sie bei der lokalen Entwicklung der Seite Tests mit einem lokalen Webserver durch und nicht nur über file://-URLs. Das Skript emrun.py im Emscripten-Quellbaum ist dafür ausgelegt, als Ad-hoc-Webserver für diesen Zweck zu dienen. Emrun ist vorkonfiguriert, um die Bereitstellung von gzip-komprimierten Dateien (mit der Endung .gz) zu handhaben, aktiviert die Unterstützung für den Large-Allocation-Header und ermöglicht automatisierte Befehlszeilenläufe von kompilierten Seiten.

  • Fangen Sie alle Ausnahmen ab, die aus Einstiegspunkten stammen, die kompilierte asm.js- und WebAssembly-Codes aufrufen. Es gibt drei verschiedene Ausnahmeklassen, die kompilierter Code werfen kann:

    1. C++-Ausnahmen, die durch eine geworfene Ganzzahl dargestellt und nicht vom C++-Programm abgefangen werden. Diese Ganzzahl zeigt auf eine Speicherstelle im Anwendungs-Heap, die einen Zeiger auf das geworfene Objekt enthält.

    2. Ausnahmen, die dadurch verursacht werden, dass die Emscripten-Laufzeit die Funktion abort() aufruft. Diese entsprechen einem fatalen Fehler, von dem sich die Ausführung des kompilierten Codes nicht erholen kann. Dies kann beispielsweise beim Aufruf eines ungültigen Funktionszeigers auftreten.

    3. Traps, die durch kompilierten WebAssembly-Code verursacht werden. Diese entsprechen fatalen Fehlern, die von der WebAssembly-VM stammen. Dies kann beispielsweise bei einer Ganzzahldivision durch Null auftreten oder beim Konvertieren einer großen Gleitkommazahl in eine Ganzzahl, wenn der Float außerhalb des Bereichs der durch diesen Ganzzahltyp darstellbaren Zahlen liegt.

  • Implementieren Sie einen finalen "Catch-All"-Error-Handler auf der Seite, indem Sie ein window.onerror-Skript implementieren. Dies wird als letzter Ausweg aufgerufen, wenn keine andere Quelle eine auf der Seite aufgetretene Ausnahme behandelt hat. Siehe window.onerror Dokumentation auf MDN.

  • Lassen Sie die HTML-Seite nicht „einfrieren“ und die Fehlermeldung in der Konsole der Webseite vergraben, da die meisten Benutzer nicht wissen werden, wie sie diese dort finden können. Bemühen Sie sich, aussagekräftige Fehlermeldungen auf der Haupt-HTML-Seite bereitzustellen, vorzugsweise mit Hinweisen zum weiteren Vorgehen. Wenn das Aktualisieren einer Browserversion oder von GPU-Treibern oder das Freigeben von Speicherplatz auf der Festplatte helfen könnte, die Seite zum Laufen zu bringen, lassen Sie den Benutzer wissen, was er ausprobieren könnte. Wenn der fragliche Fehler völlig unerwartet ist, ziehen Sie in Erwägung, einen Link oder eine E-Mail-Adresse bereitzustellen, an die das Problem gemeldet werden kann.

  • Stellen Sie aussagekräftige und interaktive Ladefortschrittsanzeigen bereit, um dem Benutzer zu zeigen, ob der Ladevorgang noch läuft und was als Nächstes passieren wird. Versuchen Sie zu verhindern, dass der Benutzer in den Zustand „Ich frage mich, ob es noch lädt oder ob es hängt?“ versetzt wird.

Vorbereitung auf die Web-Umgebung

Bei der Planung einer Testmatrix, bevor eine Website online geht, kann es eine gute Idee sein, die folgenden Punkte zu überprüfen.

  • Das Verhalten der Webseite kann subtil unterschiedlich sein, wenn sie als Top-Level-Fenster oder in einem Iframe ausgeführt wird. Stellen Sie sicher, beide Szenarien zu testen, falls diese anwendbar sind.

  • Testen Sie sowohl 32-Bit- als auch 64-Bit-Browser, insbesondere simulieren Sie Szenarien mit Speichermangel in 32-Bit-Browsern.

  • Beachten Sie die HTTP Cross-Origin Access Control-Regeln (CORS) und wie sie die von Ihnen gehostete Website-Architektur betreffen.

  • Beachten Sie die Content Security Policy-Regeln (CSP) und notieren Sie, mit welcher Art von CSP-Richtlinie die Website betrieben werden soll.

  • Achten Sie auf die Mixed Content Security-Einschränkungen, die Browser auferlegen.

  • Stellen Sie sicher, dass die Website im privaten Browsermodus (Inkognito-Modus) gut funktioniert. Dies verhindert beispielsweise, dass die Website Daten in IndexedDB persistent speichert.

  • Testen Sie, ob die Seite gut funktioniert, wenn sie in einen Hintergrund-Tab verschoben wird. Verwenden Sie die DOM-Ereignisse blur, focus und visibilitychange, um auf das Verbergen und Anzeigen der Seite zu reagieren. Dies ist insbesondere für Anwendungen relevant, die Audio wiedergeben.

  • Wenn die Seite WebGL verwendet, stellen Sie sicher, dass sie in der Lage ist, das "WebGL Context Loss"-Ereignis ordnungsgemäß zu handhaben. Verwenden Sie die Entwicklererweiterung WebGL_lose_context, um beim Testen programmatisch Kontextverlust-Ereignisse auszulösen.

  • Überprüfen Sie, ob die Seite auf Displays mit unterschiedlichen window.devicePixelRatio-Einstellungen (DPI) wie beabsichtigt funktioniert, insbesondere bei der Verwendung von WebGL. Siehe Khronos.org: HandlingHighDPI. Versuchen Sie unter Windows und macOS, die Skalierungseinstellung des Desktop-Displays zu ändern, um verschiedene Werte von window.devicePixelRatio zu testen, die der Browser meldet.

  • Testen Sie, ob verschiedene Zoomstufen der Seite das Website-Layout nicht zerstören, insbesondere wenn die Seite mit einem bereits vor-gezoomten Browserfenster aufgerufen wird.

  • Überprüfen Sie gleichermaßen, ob das Seitenlayout nicht bricht, wenn die Größe des Browserfensters geändert wird oder wenn die Website mit einem bereits initial sehr klein oder groß eingestellten Browserfenster oder einem unverhältnismäßigen Seitenverhältnis besucht wird.

  • Achten Sie, insbesondere wenn Sie auf Mobilgeräte abzielen, auf den <meta viewport>-Tag, um ein Website-Layout zu entwickeln, das auf Mobilgeräten gut funktioniert.

  • Wenn die Seite WebGL verwendet, testen Sie verschiedene GPUs auf den Zielplattformen. Überprüfen Sie insbesondere das Verhalten der Website, wenn das Fehlen benötigter WebGL-Erweiterungen und die Unterstützung komprimierter Texturformate simuliert wird.

  • Wenn Sie die requestAnimationFrame()-API verwenden (d. h. die Funktion emscripten_set_main_loop()), um das Rendering zu steuern, beachten Sie, dass die Rate, mit der die Funktion aufgerufen wird, nicht immer 60 Hz beträgt, sondern zur Laufzeit variieren kann, z. B. wenn das Browserfenster in einem Multi-Monitor-Setup von einem Display auf ein anderes verschoben wird, falls die Displays unterschiedliche Bildwiederholraten haben. Aktualisierungsintervalle wie 75Hz, 90Hz, 100Hz, 120Hz, 144Hz und 200Hz werden immer häufiger.

  • Simulieren Sie das Fehlen spezieller APIs, die die Seite benötigen könnte, z. B. Gamepad-, Beschleunigungs- oder Touch-Ereignisse, und stellen Sie sicher, dass in diesen Fällen ebenfalls ein angemessener Fehlerfluss behandelt wird.

Wenn Sie gute Tipps oder Vorschläge haben, helfen Sie bitte mit, diesen Leitfaden zu verbessern, indem Sie Feedback im Emscripten-Bug-Tracker oder auf der emscripten-discuss Mailingliste posten.