
JavaScript-Ausführungszeit reduzieren: weniger ausliefern, dann verschieben, dann aufteilen
- vuetelemetry
- Leitfäden
- 8 Min. Lesezeit
Lighthouse liefert die Zahl, aber die Lösung liegt fast nie im eigenen Code. Der Großteil der Ausführungszeit entfällt auf Abhängigkeiten, die laufen, bevor überhaupt etwas sichtbar ist. Drei Schritte in der Reihenfolge, die wirklich funktioniert, und die eine Messung, die man zuerst machen sollte.
Lighthouse meldet, dass die JavaScript-Ausführung 3,4 Sekunden gedauert hat, und bietet eine Liste von Dateien an. Der Reflex ist, den eigenen Code zu öffnen und Schleifen zu optimieren. Das ist fast immer die falsche Stelle, und der Grund ist Arithmetik: In einer typischen Anwendung ist der eigene Code nur ein kleiner Teil der Bytes, die der Browser parst, kompiliert und ausführt.
Ausführungszeit sind drei getrennte Kosten, die die Werkzeuge als eine Zahl anzeigen. Das Parsen macht aus Text einen abstrakten Syntaxbaum, das Kompilieren daraus Bytecode, und die Auswertung führt ihn aus. Alle drei wachsen mit der Menge an ausgeliefertem JavaScript, deshalb ist der erste Hebel nicht schnellerer Code, sondern weniger Code.
Erst messen, nie raten

Messen Sie, bevor Sie etwas anfassen. Öffnen Sie das Performance-Panel, zeichnen Sie einen Seitenaufruf mit vierfacher CPU-Drosselung auf und lesen Sie die Bottom-Up-Ansicht nach URL gruppiert. Sie ordnet die Millisekunden den Dateien zu. Eine Vermutung, welche Abhängigkeit teuer ist, liegt oft genug daneben, um einen Tag zu kosten.
Weniger ausliefern, vor allem anderen
Schritt eins: weniger ausliefern. Der Coverage-Tab der Chrome DevTools zeigt, welcher Anteil jeder Datei nie ausgeführt wurde. Werte über 70 Prozent ungenutzt sind häufig und deuten meist auf eine Bibliothek, die für eine einzige Funktion importiert wurde, ein Polyfill-Paket für nicht mehr unterstützte Browser oder eine ganze Datums- oder Icon-Bibliothek, wo drei Importe genügt hätten.
- Mit vierfacher CPU-Drosselung aufzeichnen und Bottom-Up nach URL lesen, bevor man etwas ändert
- Coverage-Tab: Dateien mit über 70 Prozent ungenutztem Code sind die ersten Kandidaten zum Entfernen
- Eine Bibliothek durch eine eingebaute Browserfunktion ersetzen, wo es eine gibt, Intl vor einer Datumsbibliothek
- defer für Skripte, die das DOM berühren, Drittanbieter-Tags nach dem load-Ereignis laden
- Erst nach Routen aufteilen, wenn entfernt ist, was gar nicht ausgeliefert werden sollte
Der Ersatz ist selten spektakulär. Eine 70 kB große Bibliothek zur Datumsformatierung lässt sich oft durch `Intl.DateTimeFormat` ersetzen, das nichts kostet, weil es bereits im Browser steckt. Dieser eine Tausch entfernt Parsen, Kompilieren und Auswerten auf einmal, statt sie zu beschleunigen.
Verschieben, was der erste Paint nicht braucht
Schritt zwei: verschieben, was der erste Paint nicht braucht. Analytics, Chat-Widgets, Consent-Banner und Karussell-Bibliotheken laufen alle, bevor die Seite nutzbar ist, ohne etwas dazu beizutragen. `defer` an einem Script-Tag hält den Download parallel und verlegt die Ausführung hinter das Parsen; `async` führt es aus, sobald es ankommt, was für alles, was das DOM berührt, schlechter ist.
Speziell bei Drittanbieter-Tags verschiebt das Laden nach dem load-Ereignis oder bei der ersten Interaktion ihre gesamten Kosten aus dem gemessenen Zeitfenster und vor allem aus dem Zeitfenster, in dem der Nutzer wartet.
Das Bundle aufteilen, in dieser Reihenfolge
Schritt drei: das Bundle aufteilen. Code-Splitting auf Routenebene bedeutet, dass ein Besucher der Startseite den Checkout nicht parst. In einer Vue-Anwendung übernehmen das `defineAsyncComponent` und das dynamische `import()`, und moderne Bundler teilen automatisch nach Route, wenn Routen verzögert importiert werden.
Die Reihenfolge dieser drei Schritte zählt. Ein Bundle mit 200 kB ungenutzter Bibliothek aufzuteilen, verschiebt nur den Moment, in dem man dafür bezahlt. Entfernt man zuerst die Bibliothek, wird das Aufteilen kleiner und der Gewinn größer.
Was die Zahl nicht sagt
Eine Warnung zur Zahl selbst. Total Blocking Time und Interaction to Next Paint sind das, was ein Besucher erlebt; die JavaScript-Ausführungszeit ist ein Bestandteil davon, kein Synonym. Die Ausführungszeit einer ohnehin schnellen Seite zu senken, ändert nichts Spürbares, und eine Seite kann bei der Ausführung schlecht abschneiden und sich trotzdem flüssig anfühlen, weil die Arbeit nach dem Paint stattfindet.
Felddaten entscheiden. Ein Labortest auf einem schnellen Laptop verbirgt genau die Gruppe, die leidet: Android-Mittelklassetelefone in einem überlasteten Netz, wo 300 Millisekunden Parsen zu zwei Sekunden werden. Sind die Felddaten gut und die Labordaten hässlich, glauben Sie dem Feld.
Bevor Sie Skripte optimieren, lohnt sich die Prüfung, ob die Bytes überhaupt komprimiert werden: gzip vs. Brotli, und was wir bei der Messung von 336 öffentlichen Stellen gefunden haben.
FAQ
Was verursacht eine hohe JavaScript-Ausführungszeit?
Die Menge an JavaScript, die der Browser parsen, kompilieren und auswerten muss, und die in den meisten Projekten von Abhängigkeiten dominiert wird, nicht vom Anwendungscode. Lange Tasks auf dem Main Thread beim Start, typischerweise die Framework-Initialisierung und Drittanbieter-Tags, machen aus dieser Menge eine Zahl, die der Nutzer spürt.
Ist die Ausführungszeit dasselbe wie die Total Blocking Time?
Nein. Die Ausführungszeit ist, wie lange Skripte laufen; die Total Blocking Time misst, wie lange der Main Thread zwischen First Contentful Paint und Interaktivität am Stück länger als 50 Millisekunden blockiert war. Die Ausführungszeit ist ein Bestandteil davon, und Arbeit nach dem Paint kann Ausführungszeit hinzufügen, ohne Blockierzeit hinzuzufügen.
Verringern defer oder async die Ausführungszeit?
Keines von beiden verringert die Arbeit; beide verschieben, wann sie stattfindet. defer führt das Skript nach dem Parsen des HTML in Dokumentreihenfolge aus, was das Rendern nicht blockiert und der richtige Standard für alles ist, was das DOM berührt. async führt es aus, sobald es geladen ist, in unvorhersehbarer Reihenfolge.
Wie viel JavaScript ist zu viel?
Es gibt keinen universellen Schwellenwert, und jede Zahl, die als solcher genannt wird, ist eine Faustregel. Der nützliche Test sind Felddaten: Liegen Interaction to Next Paint und Largest Contentful Paint für echte Nutzer auf echten Geräten innerhalb ihrer Schwellen, ist die ausgelieferte Menge vertretbar, egal was die Byte-Zahl sagt.
Sollte ich mein Framework aufgeben, um das zu beheben?
Selten, und nicht als ersten Schritt. Ungenutzte Abhängigkeiten zu entfernen, Drittanbieter-Tags zu verschieben und nach Routen aufzuteilen, holt meist mehr Zeit zurück als eine Framework-Migration, bei einem Bruchteil des Risikos. Ziehen Sie die Migration erst in Betracht, wenn die Messung nach diesen drei Schritten auf die Framework-Initialisierung selbst zeigt.



Die Reihenfolge dieser drei Schritte zählt. Ein Bundle mit 200 kB ungenutzter Bibliothek aufzuteilen, verschiebt nur den Moment, in dem man dafür bezahlt. Entfernt man zuerst die Bibliothek, wird das Aufteilen kleiner und der Gewinn größer.