Zum Inhalt springen
Core Web Vitals Spezialisten
Alle Artikel Performance

JavaScript und Performance Budgets: Weniger Code

14 Min. Lesezeit
JavaScriptPerformance BudgetsINPCore Web Vitals

JavaScript ist der teuerste Ressourcentyp im Web. Während Bilder nur heruntergeladen und dekodiert werden müssen, muss JavaScript heruntergeladen, geparst, kompiliert und ausgeführt werden – jeder dieser Schritte blockiert den Main Thread des Browsers und verzögert die Interaktivität der Seite. Laut HTTP Archive laden Websites im Median 509 KB komprimiertes JavaScript pro Seitenaufruf (Quelle: HTTP Archive, 2025), was unkomprimiert oft 1,5 bis 2,5 MB entspricht. Das Ergebnis: 70 Prozent der Websites verfehlen den empfohlenen INP-Schwellenwert von 200 Millisekunden (Quelle: Chrome UX Report, 2025). Dieser Artikel zeigt, wie Performance Budgets, Code Splitting und gezieltes Third-Party-Management die JavaScript-Last reduzieren und die Core Web Vitals nachhaltig verbessern.

JavaScript Performance Budget PipelineSource Bundle1.8 MB JavaScript (unkomprimiert)145 npm-PaketeTBT: 3200ms | INP: 450msOptimierungTree ShakingCode SplittingDead Code EliminationOptimiertes Bundle380 KB JavaScript (komprimiert)42 npm-PaketeTBT: 280ms | INP: 85msPerformance Budget: max. 150 KB JS (initial, komprimiert)Critical PathFramework Core: 45 KBRouter: 8 KBCritical CSS-in-JS: 12 KBGesamt: 65 KBLazy ChunksProduktseite: 28 KBCheckout: 35 KBSuche: 18 KBOn-Demand geladenThird-PartyAnalytics: 22 KBConsent: 15 KBChat-Widget: 45 KBBudget-Risiko!Bundle-AnalyseTreemap-VisualisierungDuplikat-ErkennungImport-Cost-TrackingCI/CD-Integration-79%JS-Bundle reduziert200msINP-Zielwert150KBInitial-JS-Budget3xschnellere InteraktionOptimierungspfad1. Audit und Messen2. Budget definieren3. Split und Shake4. Third-Party prüfen5. CI/CD-Checks

Warum JavaScript die teuerste Web-Ressource ist

Der Unterschied zwischen JavaScript und anderen Ressourcen liegt in den Verarbeitungskosten. Ein 200 KB großes JPEG-Bild benötigt Millisekunden für das Dekodieren und Rendern. 200 KB komprimiertes JavaScript hingegen müssen zunächst dekomprimiert werden (typischerweise auf 600-800 KB), dann geparst, in Bytecode kompiliert und schließlich ausgeführt werden. Auf einem durchschnittlichen Mittelklasse-Smartphone dauert die Verarbeitung von 200 KB komprimiertem JavaScript 1 bis 2 Sekunden – Sekunden, in denen der Main Thread blockiert ist und die Seite auf keine Nutzereingabe reagieren kann.

Diese Blockierung des Main Threads beeinflusst direkt den Interaction to Next Paint (INP), der seit März 2024 First Input Delay (FID) als Core Web Vital ersetzt hat. INP misst die Latenz aller Nutzerinteraktionen – Klicks, Taps und Tastatureingaben – während des gesamten Seitenaufenthalts und berichtet die schlechteste Interaktion (am 98. Perzentil). Google definiert einen INP unter 200 Millisekunden als gut. JavaScript-schwere Seiten überschreiten diesen Wert regelmäßig, weil lange Aufgaben (Long Tasks) den Main Thread blockieren und Interaktionen warten müssen.

Die Auswirkungen sind messbar: Eine Studie von Akamai zeigt, dass jede zusätzliche Sekunde JavaScript-Verarbeitungszeit die Conversion Rate um 4,4 Prozent senkt (Quelle: Akamai, 2024). Für einen Online-Shop mit 100.000 Euro Monatsumsatz bedeutet eine halbe Sekunde unnötige JavaScript-Verarbeitung einen potenziellen Umsatzverlust von 2.200 Euro pro Monat. Die Performance-Analyse der JavaScript-Last ist damit ein direkter Hebel für den Geschäftserfolg.

Performance Budgets: Grenzen setzen, bevor es zu spät ist

Ein Performance Budget definiert Obergrenzen für Ressourcen, die eine Website laden darf. Das Konzept ist simpel, aber wirkungsvoll: Statt Performance-Probleme reaktiv zu beheben, nachdem sie aufgetreten sind, verhindert ein Budget proaktiv, dass sie überhaupt entstehen. Die effektivsten Budgets definieren Limits auf drei Ebenen: Dateigröße (maximale Bytes pro Ressourcentyp), Ladezeit (maximale Metriken wie LCP, TBT) und Komplexität (maximale Anzahl von Requests oder npm-Paketen).

Für JavaScript empfehlen wir ein initiales Budget von maximal 150 KB komprimiertem JavaScript für den Critical Path – das ist der Code, der für die erste interaktive Darstellung der Seite benötigt wird. Dieses Budget leitet sich direkt aus dem INP-Zielwert ab: 150 KB komprimiert entsprechen circa 450 KB unkomprimiert, die auf einem durchschnittlichen Smartphone in etwa 900 Millisekunden verarbeitet werden. Addiert man Netzwerklatenz und andere Rendering-Kosten, bleibt genug Spielraum für einen INP unter 200 Millisekunden.

Die Durchsetzung des Budgets erfolgt idealerweise in der CI/CD-Pipeline. Build-Tools wie webpack und Vite unterstützen Performance-Hints, die den Build abbrechen, wenn ein konfiguriertes Größenlimit überschritten wird. Ergänzend können Tools wie Lighthouse CI bei jedem Pull Request automatisch einen Performance-Audit durchführen und eine Warnung ausgeben, wenn Budget-Grenzen gerissen werden. Diese Automatisierung ist entscheidend – ohne automatische Prüfung schleichen sich Performance-Regressionen unweigerlich ein.

Größen-Budget

Maximal 150 KB komprimiertes JavaScript für den Critical Path. Zusätzliche Chunks werden lazy geladen und zählen nicht gegen das initiale Budget. Überwacht per CI/CD.

Zeit-Budget

Total Blocking Time unter 300 Millisekunden, INP unter 200 Millisekunden. Diese Metriken korrelieren direkt mit der wahrgenommenen Reaktionsfähigkeit der Website.

Abhängigkeits-Budget

Maximale Anzahl von npm-Paketen und maximale Gesamtgröße der node_modules. Verhindert unkontrolliertes Wachstum der Dependency-Kette und reduziert Sicherheitsrisiken.

Code Splitting: Nur laden, was wirklich gebraucht wird

Code Splitting teilt das JavaScript-Bundle in kleinere Chunks auf, die einzeln und bedarfsgesteuert geladen werden. Statt den gesamten Anwendungscode beim initialen Seitenaufruf zu laden, wird nur der Code geladen, der für die aktuelle Seite benötigt wird. Alle anderen Module werden erst geladen, wenn der Nutzer die entsprechende Funktionalität aufruft – etwa beim Navigieren zu einer neuen Seite oder beim Öffnen eines Modals.

Die wichtigste Form des Code Splittings ist das Route-Based Splitting: Jede Seite oder Route erhält einen eigenen Chunk, der nur geladen wird, wenn der Nutzer diese Route besucht. Moderne Frameworks wie SvelteKit, Next.js und Nuxt implementieren Route-Based Splitting automatisch. Für Shopware-basierte Frontends muss das Splitting manuell konfiguriert werden, indem Plugin-Funktionalitäten in separate Entry Points aufgeteilt werden.

Component-Based Splitting geht einen Schritt weiter und lädt einzelne Komponenten erst bei Bedarf. Ein Beispiel: Das Chat-Widget einer Website wird nur geladen, wenn der Nutzer auf den Chat-Button klickt. Das Bewertungsformular wird nur geladen, wenn der Nutzer zum Bewertungsabschnitt scrollt. Diese Technik reduziert den initialen JavaScript-Download drastisch, erfordert aber sorgfältige Planung, damit die Verzögerung beim Nachladen nicht die Nutzererfahrung beeinträchtigt.

Das Prefetching ergänzt Code Splitting um vorausschauendes Laden. Der Browser lädt Chunks, die der Nutzer wahrscheinlich bald benötigen wird, im Hintergrund vor – etwa den Code der nächsten Seite, wenn der Nutzer über einen Link hovert. und die Intersection Observer API ermöglichen dieses vorausschauende Laden, ohne die aktuelle Seitenperformance zu beeinträchtigen. Das Ergebnis: Der Nutzer erlebt das Code Splitting nicht als Verzögerung, weil der benötigte Code bereits im Cache liegt, wenn er ihn braucht.

Tree Shaking: Ungenutzten Code automatisch eliminieren

Tree Shaking ist eine Build-Optimierung, die ungenutzten Code aus dem finalen Bundle entfernt. Der Name kommt von der Metapher, einen Baum zu schütteln, damit tote Blätter abfallen. In der Praxis analysiert der Bundler die Import-Kette des gesamten Codes und entfernt alle Exporte, die nirgends importiert werden. Diese Dead-Code-Elimination kann die Bundle-Größe um 20 bis 50 Prozent reduzieren, abhängig davon, wie viel ungenutzter Code in den Abhängigkeiten steckt.

Effektives Tree Shaking setzt voraus, dass alle Module das ES-Module-Format (import/export) verwenden. CommonJS-Module (require/module.exports) können nicht effektiv ge-tree-shaked werden, da ihre Exporte dynamisch sind und erst zur Laufzeit feststehen. Bei der Wahl von npm-Paketen sollte deshalb auf das Vorhandensein eines module- oder exports-Feldes in der package.json geachtet werden – das signalisiert, dass das Paket ES-Module bereitstellt.

Ein häufiger Stolperstein ist die Side-Effect-Markierung. Module, die beim Import Seiteneffekte ausführen – etwa globale Variablen setzen oder CSS injizieren – dürfen nicht vom Tree Shaking entfernt werden, auch wenn ihre Exporte nicht genutzt werden. Die sideEffects-Eigenschaft in der package.json teilt dem Bundler mit, welche Module sicher entfernt werden können. Falsch gesetzte oder fehlende Side-Effect-Markierungen sind einer der häufigsten Gründe, warum Tree Shaking nicht den erwarteten Effekt zeigt.

Third-Party-Scripts: Der versteckte Performance-Killer

Eigener Code macht oft nur einen Bruchteil der JavaScript-Last einer Website aus. Analytics-Tracker, Consent-Manager, Chat-Widgets, Retargeting-Pixel, Social-Media-Einbindungen und A/B-Testing-Tools addieren sich schnell zu Hunderten von Kilobytes. Laut einer Analyse des Web Almanac laden Websites im Median 22 Third-Party-Scripts (Quelle: Web Almanac, 2024), die zusammen 40 bis 60 Prozent der gesamten JavaScript-Last ausmachen.

Das Problem mit Third-Party-Scripts ist die mangelnde Kontrolle. Der eigene Code kann optimiert, gesplittet und komprimiert werden – Third-Party-Scripts werden extern gehostet und können bei jedem Update größer, langsamer oder instabiler werden, ohne dass der Website-Betreiber davon erfährt. Ein einzelnes schlecht implementiertes Chat-Widget kann den INP einer ansonsten optimierten Website verdoppeln. Eine professionelle Performance-Analyse deckt diese versteckten Kosten systematisch auf.

Die Lösung beginnt mit einer vollständigen Bestandsaufnahme aller Third-Party-Scripts und ihrer Performance-Kosten. Für jedes Script wird die Dateigröße, die Main-Thread-Blockierzeit und der Einfluss auf die Core Web Vitals dokumentiert. Anschließend wird jedes Script einer von drei Kategorien zugeordnet: essentiell (Consent-Manager, Analytics), optional mit Mehrwert (Live-Chat, Bewertungen) oder verzichtbar (Social-Media-Buttons, überflüssige Tracker). Scripts der dritten Kategorie werden entfernt, Scripts der zweiten Kategorie werden per Lazy Loading oder User-Interaktion nachgeladen. Diese Priorisierung ist ein zentraler Bestandteil jeder Server- und Infrastruktur-Optimierung.

Script-TypTypische GrößeMain-Thread-ImpactEmpfehlung
Analytics-Tracker20-45 KBMittel (100-300ms)Async laden, First-Party-Proxy
Consent-Manager15-80 KBHoch (200-500ms)Leichtgewichtiges Tool wählen
Chat-Widget40-200 KBSehr hoch (500-1500ms)Lazy Load bei Klick
A/B-Testing30-100 KBHoch (300-800ms)Server-Side-Testing bevorzugen
Social-Media-Buttons50-300 KBMittel (200-600ms)Statische Links verwenden
Retargeting-Pixel5-20 KBNiedrig (50-150ms)Nach Consent lazy laden

Bundle-Analyse: Transparenz über jeden Kilobyte

Bevor Optimierungen wirken können, muss die aktuelle JavaScript-Zusammensetzung verstanden werden. Bundle-Analyse-Tools visualisieren die Größe jedes Moduls im Bundle als Treemap und machen sofort sichtbar, welche Abhängigkeiten den größten Anteil haben. Tools wie webpack-bundle-analyzer, vite-bundle-visualizer und source-map-explorer erzeugen interaktive Treemaps, die zeigen, wohin jedes Byte geht.

Typische Erkenntnisse aus einer Bundle-Analyse sind: Duplizierte Abhängigkeiten – verschiedene Versionen derselben Bibliothek im Bundle, weil transitive Abhängigkeiten unterschiedliche Versionen fordern. Übergroße Importe – ein einzelner Import aus einer Utility-Bibliothek zieht die gesamte Bibliothek ins Bundle, weil das Tree Shaking nicht greift. Unnötige Polyfills – Polyfills für Features, die alle Zielbrowser bereits nativ unterstützen. Jeder dieser Punkte bietet konkretes Einsparpotenzial.

Die Integration der Bundle-Analyse in die CI/CD-Pipeline macht Performance-Regressionen sofort sichtbar. Bei jedem Pull Request wird ein Bundle-Report erzeugt und mit dem Main-Branch verglichen. Wächst das Bundle über das definierte Budget, wird der Build markiert und der Entwickler muss die Ursache untersuchen, bevor der Code gemergt werden kann. Diese Feedback-Schleife verhindert das schleichende Wachstum der JavaScript-Last über Wochen und Monate.

INP-Optimierung: Long Tasks vermeiden und aufbrechen

Der Interaction to Next Paint (INP) wird maßgeblich von Long Tasks beeinflusst – JavaScript-Aufgaben, die den Main Thread länger als 50 Millisekunden blockieren. Während einer Long Task kann der Browser auf keine Nutzereingabe reagieren: Klicks werden verzögert, Scrolling ruckelt und die Seite fühlt sich träge an. Die Optimierung des INP erfordert das Identifizieren und Aufbrechen dieser Long Tasks.

Das Yielding-Pattern ist die wichtigste Technik zur INP-Verbesserung. Statt eine lange Berechnung in einem Stück auszuführen, wird sie in kleinere Teilaufgaben zerlegt, zwischen denen der Browser die Möglichkeit erhält, auf Nutzereingaben zu reagieren. Die scheduler.yield()-API (ab 2025 in allen modernen Browsern verfügbar) pausiert die aktuelle Aufgabe und gibt dem Browser die Chance, anstehende Interaktionen zu verarbeiten, bevor die Aufgabe fortgesetzt wird.

Weitere Strategien zur INP-Optimierung umfassen: Event-Handler schlank halten – komplexe Logik in Event-Handlern sollte in requestAnimationFrame oder requestIdleCallback ausgelagert werden. State-Updates bündeln – mehrere DOM-Änderungen in einem Rendering-Zyklus zusammenfassen statt einzeln auszuführen. Web Worker einsetzen – rechenintensive Aufgaben wie Datenverarbeitung oder Kryptografie in einen Web Worker auslagern, der in einem eigenen Thread läuft und den Main Thread nicht blockiert.

Praxistipp: INP-Debugging mit der Performance-API

Nutzen Sie die PerformanceObserver-API mit dem Typ 'event', um alle langsamen Interaktionen im Feld zu erfassen. Jeder Eintrag enthält die Interaktionslatenz, den Event-Typ und das Zielelement. Aggregieren Sie diese Daten, um die Seiten und Elemente mit den schlechtesten INP-Werten zu identifizieren. So können Sie gezielt die problematischsten Interaktionen optimieren.

Moderne JavaScript-Auslieferung: Module, defer und async

Die Art, wie JavaScript eingebunden wird, beeinflusst die Performance ebenso stark wie die Dateigröße. Ein reguläres