Da wir Websites stärker abhängig von JavaScript erstellen, zahlen wir manchmal für das, was wir senden, auf eine Weise, die wir nicht immer sehen können. In diesem Artikel erfahren Sie, warum ein wenig Disziplin Ihnen dabei helfen kann, Ihre Website auf Mobilgeräten schnell zu laden und interaktiv zu gestalten. Wenn Sie weniger JavaScript ausliefern, reduziert sich der Zeitaufwand für die Netzwerkübertragung, das Dekomprimieren von Code und das Parsing und Kompilieren des JavaScript-Codes.
Netzwerk
Die meisten Entwickler denken bei den Kosten für JavaScript an die Download- und Ausführungskosten. Je langsamer die Verbindung des Nutzers ist, desto länger dauert das Senden von mehr JavaScript-Byte.
Das kann zu einem Problem werden, da der effektive Netzwerkverbindungstyp eines Nutzers möglicherweise nicht 3G, 4G oder WLAN ist. Sie können im Café-WLAN, aber mit einem mobilen Hotspot mit 2G-Geschwindigkeiten verbunden sein.
Sie können die Kosten für die Netzwerkübertragung von JavaScript verringern:
- Nur den Code senden, den der Nutzer benötigt:
- Verwenden Sie die Codeaufteilung, um den JavaScript-Code in wichtige und nicht wichtige Elemente aufzuteilen. Modul-Bundler wie webpack unterstützen die Codeaufteilung.
- Lazy Loading in nicht kritischem Code.
- Reduzierung
- Verwenden Sie UglifyJS, um ES5-Code zu reduzieren.
- Verwenden Sie babel-minify oder uglify-es, um ES2015+ zu reduzieren.
- Komprimierung
- Entfernen Sie nicht verwendeten Code.
- Ermitteln Sie mit der Codeabdeckung in den Entwicklertools Möglichkeiten für Code, der entfernt oder verzögert geladen werden kann.
- Verwenden Sie babel-preset-env und Browserlist, damit Funktionen, die bereits in modernen Browsern vorhanden sind, nicht transpiliert werden. Fortgeschrittene Entwickler können mithilfe einer sorgfältigen Analyse ihrer Webpack-Bundles Möglichkeiten ermitteln, um nicht benötigte Abhängigkeiten zu reduzieren.
- Informationen zum Entfernen von Code finden Sie unter Tree-Shaking, den erweiterten Optimierungen von Closure Compiler und den Plug-ins zum Trimmen von Bibliotheken wie lodash-babel-plugin oder ContextReplacementPlugin von Webpack für Bibliotheken wie Moment.js.
- Code im Cache speichern, um Netzwerkfahrten zu minimieren.
- Verwenden Sie HTTP-Caching, damit Browser Antworten effektiv im Cache speichern. Legen Sie die optimale Lebensdauer für Skripts fest (max-age) und geben Sie Validierungstokens (ETag) an, um die Übertragung unveränderter Byte zu vermeiden.
- Das Service-Worker-Caching kann Ihr Anwendungsnetzwerk stabil machen und Ihnen einen schnellen Zugriff auf Funktionen wie den Code-Cache von V8 ermöglichen.
- Verwenden Sie langfristiges Caching, damit nicht geänderte Ressourcen nicht noch einmal abgerufen werden müssen. Wenn Sie Webpack verwenden, finden Sie weitere Informationen unter Hashing von Dateinamen.
Parsen/Kompilieren
Nach dem Download verursacht eine JavaScript-Engine die schwersten Kosten für JavaScript, den Code zu parsen/zu kompilieren. In den Chrome-Entwicklertools ist das Parsen und Kompilieren Teil der gelben „Scripting“-Zeit im Bereich „Leistung“.
Die Tabs Bottom-Up- und Aufruf-Baum zeigen die genauen Parse-/Compile-Zeiten an:
Aber warum ist das wichtig?
Wenn Sie viel Zeit mit dem Parsen oder Kompilieren von Code verbringen, kann dies dazu führen, dass ein Nutzer deutlich länger mit Ihrer Website interagieren kann. Je mehr JavaScript-Elemente Sie senden, desto länger dauert das Parsen und Kompilieren, bis Ihre Website interaktiv wird.
Byte für Byte ist die Verarbeitung von JavaScript durch den Browser teurer als die gleichwertige Größe eines Bildes oder einer Web-Schriftart – Tom Dale
Im Vergleich zu JavaScript fallen hohe Kosten für die Verarbeitung von Bildern gleicher Größe an (sie müssen immer noch decodiert werden!). Bei durchschnittlicher mobiler Hardware ist jedoch die Wahrscheinlichkeit höher, dass JavaScript die Interaktivität einer Seite beeinträchtigt.
Wenn das Parsen und Kompilieren langsam ist und der Kontext wichtig ist, sprechen wir hier über durchschnittliche Smartphones. Durchschnittliche Nutzer können Smartphones mit langsamen CPUs und GPUs, ohne L2/L3-Cache und sogar mit beschränktem Arbeitsspeicher haben.
Netzwerk- und Gerätefunktionen stimmen nicht immer überein. Ein Nutzer mit einer erstklassigen Glasfaserverbindung hat nicht unbedingt die optimale CPU, um den an sein Gerät gesendeten JavaScript-Code zu parsen und auszuwerten. Dies gilt auch für umgekehrte... eine schlechte Netzwerkverbindung, aber eine extrem schnelle CPU. – Kristofer Baxter, LinkedIn
Unten sind die Kosten für das Parsen von ca. 1 MB dekomprimierten (einfachen) JavaScript-Code auf Low-End- und High-End-Hardware aufgeführt. Zwischen den schnellsten Smartphones auf dem Markt und durchschnittlichen Smartphones besteht ein 2- bis 5-mal schnellerer zeitlicher Abstand zum Parsen/Kompilieren von Code.
Wie sieht es mit einer realen Website wie CNN.com aus?
Auf dem High-End-iPhone 8 dauert das Parsen/Kompilieren des JS-Codes von CNN nur etwa 4 Sekunden. Bei einem durchschnittlichen Smartphone (Moto G4) sind es etwa 13 Sekunden. Dies kann sich erheblich darauf auswirken, wie schnell ein Nutzer vollständig mit dieser Website interagieren kann.
Dies unterstreicht, wie wichtig es ist, Tests auf durchschnittlicher Hardware (z. B. Moto G4) statt nur auf dem Smartphone zu testen, das sich in der Hosentasche befindet. Der Kontext ist aber wichtig: Optimieren Sie Ihre App an die Geräte- und Netzwerkbedingungen Ihrer Nutzer.
Senden wir wirklich zu viel JavaScript nach unten? Eher, vielleicht :)
Mit HTTP Archive (die rund 500.000 Top-Websites) zur Analyse des Status von JavaScript auf Mobilgeräten haben wir herausgefunden, dass bei 50% der Websites mehr als 14 Sekunden benötigt werden, um interaktiv zu werden. Diese Websites benötigen bis zu vier Sekunden, um JS nur zu parsen und zu kompilieren.
Wenn Sie die Zeit zum Abrufen und Verarbeiten von JavaScript und anderen Ressourcen berücksichtigen, überrascht es vielleicht nicht, dass Nutzer eine Weile warten müssen, bevor sie das Gefühl haben, dass die Seiten einsatzbereit sind. Das können wir hier definitiv besser machen.
Wenn du nicht kritisches JavaScript von deinen Seiten entfernst, kannst du die Übertragungszeiten, das CPU-intensives Parsen und Kompilieren sowie den potenziellen Speicheraufwand reduzieren. Dies trägt auch dazu bei, Ihre Seiten schneller interaktiver zu gestalten.
Ausführungszeit
Nicht nur das Parsen und Kompilieren kann Kosten verursachen. Die JavaScript-Ausführung (der Code wird nach dem Parsen/Kompilieren ausgeführt) ist einer der Vorgänge, der im Hauptthread ausgeführt werden muss. Lange Ausführungszeiten können auch dazu führen, wie schnell ein Nutzer mit Ihrer Website interagieren kann.
Wenn die Ausführung des Skripts länger als 50 ms dauert, verzögert sich die Zeit bis zur Interaktivität um die gesamte Zeit, die für das Herunterladen, Kompilieren und Ausführen des JS erforderlich ist (Alex Russell).
Um dieses Problem zu beheben, sollte JavaScript in kleinen Blöcken enthalten sein, damit der Hauptthread nicht gesperrt wird. Prüfen Sie, ob Sie den Arbeitsaufwand während der Ausführung reduzieren können.
Sonstige Kosten
JavaScript kann die Seitenleistung auf andere Weise beeinflussen:
- Erinnerung Seiten können aufgrund der automatischen Speicherbereinigung (GC) den Anschein erwecken oder pausieren. Wenn ein Browser Arbeitsspeicher freimacht, wird die JS-Ausführung angehalten. So kann ein Browser, der häufig Speicher sammelt, die Ausführung häufiger unterbrechen, als es uns gefallen könnte. Vermeiden Sie Speicherlecks und häufige GCS-Pausen, um Verzögerungen auf den Seiten zu vermeiden.
- Während der Laufzeit kann JavaScript mit langer Ausführungszeit den Hauptthread blockieren und nicht mehr reagierende Seiten verursachen. Durch die Aufteilung der Arbeit in kleinere Teile (mit
requestAnimationFrame()
oderrequestIdleCallback()
für die Planung) können Reaktionsprobleme minimiert und Interaction to Next Paint (INP) verbessert werden.
Muster zur Reduzierung der JavaScript-Auslieferungskosten
Wenn du versuchst, das Parsen/Kompilieren und die Netzwerkübertragungszeit für JavaScript langsam zu halten, gibt es Muster, die dabei helfen können, wie das routenbasierte Aufteilen oder PRPL.
PRPL
PRPL (Push, Render, Pre-Cache, Lazy Load) ist ein Muster, das durch aggressives Code-Splitting und Caching für die Interaktivität optimiert wird:
Sehen wir uns die möglichen Auswirkungen an.
Wir analysieren die Ladezeit beliebter mobiler Websites und progressiver Web-Apps mithilfe der Laufzeitaufrufstatistiken von V8. Wie Sie sehen, ist die orangefarbene Parsing-Zeit ein bedeutender Teil davon, wo viele dieser Websites ihre Zeit verbringen:
Wego, eine Website, die PRPL verwendet, ermöglicht eine niedrige Parsing-Zeit für ihre Routen und wird sehr schnell interaktiv. Viele der anderen Websites oben haben Codeaufteilungs- und Leistungsbudgets eingeführt, um ihre JS-Kosten zu senken.
Progressives Bootstrapping
Viele Websites optimieren die Sichtbarkeit von Inhalten, allerdings auf Kosten der Interaktivität. Bei großen JavaScript-Bundles verwenden Entwickler manchmal serverseitiges Rendering, um bei großen JavaScript-Bundles einen schnellen First Paint zu erzielen. Dann „aktualisieren“ Sie ihn und hängen Event-Handler an, wenn der JavaScript-Code schließlich abgerufen wird.
Aber Vorsicht – das hat seine eigenen Kosten. 1) Sie senden in der Regel eine größere HTML-Antwort, was die Interaktivität erhöhen kann. 2) Sie können den Nutzer in ein überwältigendes Tal versetzen, in dem die Hälfte der Website erst nach Abschluss der JavaScript-Verarbeitung interaktiv sein kann.
Progressives Bootstrapping ist möglicherweise ein besserer Ansatz. Senden Sie eine Seite mit minimalem Funktionsumfang nach unten, die nur aus dem HTML/JS/CSS-Code besteht, der für die aktuelle Route benötigt wird. Je mehr Ressourcen verfügbar sind, desto mehr Funktionen kann die App per Lazy Loading laden.
Das Laden von Code im Verhältnis zu dem, was zu sehen ist, ist der heilige Gral. PRPL und progressives Bootstrapping sind Muster, die dabei helfen können.
Ergebnisse
Die Übertragungsgröße ist für Low-End-Netzwerke von entscheidender Bedeutung. Die Analysezeit ist für CPU-gebundene Geräte wichtig. Eine einfache Wahrnehmung ist wichtig.
Teams konnten mit strengen Leistungsbudgets Erfolg haben, um die JavaScript-Übertragungs- und -Parsing-/Kompilierungszeiten niedrig zu halten. Alex Russell's Can You Afford It?: Reale Real-World-Budgets für die Webleistung finden Sie Hinweise zu Budgets für Mobilgeräte.
Wenn du eine Website für Mobilgeräte erstellst, solltest du die Entwicklung auf repräsentativer Hardware vornehmen, deine JavaScript-Parsing-/Kompilierzeiten niedrig halten und ein Leistungsbudget einführen, damit dein Team die JavaScript-Kosten im Auge behalten kann.
Weitere Informationen
- Chrome Dev Summit 2017 – Best Practices für das moderne Laden
- Leistung von JavaScript-Start-ups
- Solving the web performance crisis – Nolan Lawson
- Können Sie es sich leisten? Reale Leistungsbudgets – Alex Russell
- Evaluating Web Frameworks and Bibliotheken – Kristofer Baxter
- Die Ergebnisse von Cloudflare zum Experimentieren mit Brotli für die Komprimierung (Hinweis: Dynamisches Brotli mit höherer Qualität kann das anfängliche Seitenrendering verzögern. Beurteilen Sie dies sorgfältig. Sie sollten stattdessen eine statische Komprimierung vornehmen.)
- Performance-Futures – Sam Saccone