Bilder, Fonts, Scripts: Performance-Tuning ohne alles neu bauen

Bilder, Fonts, Scripts: Performance-Tuning

Du brauchst keine neue Architektur, um schneller zu werden. Meist reichen 10–20% saubere Asset-Disziplin: Bilder, Fonts und Scripts – korrekt priorisiert.

„Wir müssten das komplett neu bauen.“ Klassiker. Und ja: manchmal stimmt’s. Aber in der Praxis hängen viele Performance-Probleme nicht an deinem Framework, sondern an drei langweiligen Dingen: Bilder, Fonts und Scripts.

Wenn das Hero-Bild zu spät kommt, ist LCP im Eimer. Wenn Scripts den Main Thread blockieren, leidet INP. Und wenn Fonts/Bilder/Ads keinen Platz reservieren, springt das Layout: CLS.

Dieser Beitrag ist bewusst „ohne Religionskrieg“. Keine „switch to X“-Predigt. Du bekommst Quick Wins, die du in fast jedem Stack umsetzen kannst – egal ob CMS, SPA, SSR oder „wir haben da mal was gebaut“.

Erst messen, dann schrauben (sonst optimierst du am Problem vorbei)

Bevor du irgendwas anfasst: einmal sauber gucken. PageSpeed Insights kombiniert Felddaten (CrUX, wenn verfügbar) und Lab-Daten (Lighthouse). Lab ist kontrolliert und gut zum Debuggen, Field ist die echte Welt (Geräte, Netze, Nutzerverhalten). Genau diese Unterscheidung ist der Grund, warum „Lighthouse ist grün“ nicht automatisch bedeutet, dass Nutzer happy sind.

Mini-Setup (15 Minuten, reicht für den Start)

  1. PageSpeed Insights: Checke eine typische URL (nicht nur Startseite).
  2. Lighthouse (DevTools): Reproduzierbar testen, dann gezielt Fix verifizieren.
  3. Fokus setzen: Ist das Problem eher LCP (Loading), INP (Interaktion) oder CLS (Layout)?

Merksatz: Field ist dein Kompass, Lab ist dein Schraubenschlüssel.

Bilder: Der LCP-Hebel, den fast jeder falsch zieht

In vielen Projekten ist das LCP-Element ein Bild (Hero, Produktbild, Header-Visual). Wenn dieses Bild zu groß, zu spät oder falsch priorisiert ist, kannst du an CSS/JS rumfeilen bis Weihnachten – LCP bleibt mies.

1) Responsive ausliefern: weniger Bytes, schneller sichtbar

Das Ziel ist simpel: Mobile bekommt keine 2400px-Brechstange. Responsive Images (srcset/sizes) sind dafür der Standardweg. Wenn du das nicht sauber machst, schiebst du unnötige Bytes durchs Netz und verlierst Zeit im Decoding.

2) Lazy Loading: ja – aber nicht fürs Wichtigste

Lazy Loading ist perfekt für Bilder unterhalb des sichtbaren Bereichs. Aber wenn du das LCP/Hero-Bild lazy lädst, verzögerst du genau das Element, das schnell da sein muss.

3) Priorisieren: preload (smart) statt „der Browser wird’s schon richten“

Wenn das Hero-Bild spät entdeckt wird (z.B. wegen CSS background image, späten Komponenten, request chains), kann preload helfen. Für responsive Images gibt’s sogar einen eigenen Ansatz: preload responsive images, damit der Browser früh die richtige Variante aus srcset wählen kann.

4) fetchpriority: wenn du dem Browser einen Schubs geben musst

Es gibt Fälle, wo du dem Browser sagen willst: „Dieses Asset ist wichtig.“ Die Fetch Priority API beschreibt genau diese Idee und wie man Prioritäten anhebt, ohne alles synchron zu blockieren. Für LCP-Bilder (besonders als CSS background) kann das relevant sein – aber nur gezielt einsetzen, nicht als Dauer-Button-Mash.

Quick Win Checkliste (Bilder)

  • Ist das LCP-Element ein Bild? (PSI/Lighthouse zeigen das oft klar.)
  • Ist das Hero-Bild nicht lazy?
  • Werden für Mobile kleinere Varianten ausgeliefert?
  • Ist die Dateigröße plausibel (Kompression/Format)?
  • Gibt’s request chains, die das Bild verzögern (z.B. CSS/JS-Abhängigkeiten)?

Fonts: schnell, stabil, ohne CLS-Gespringe

Fonts sind Performance-Fallen, weil sie zwei Dinge gleichzeitig beeinflussen: Ladezeit (bis Text „final“ aussieht) und Stabilität (Layout Shift). Wenn deine Fallback-Schrift ganz anders misst als die Webfont, ruckelt’s sichtbar.

1) Weniger ist mehr: Schnitte/Weights ausmisten

Viele Projekte laden 6–12 Font-Files, weil „Design wollte das so“. Real Talk: Du brauchst oft 2–4. Jeder zusätzliche Schnitt ist ein weiterer Download, weiteres Parsing, mehr Risiko für späte Umschaltung.

2) Früh entdecken: preload für echte Schlüssel-Fonts

Wenn Text-LCP bei dir relevant ist (z.B. große Headline), kann es helfen, Fonts früh zu laden. Die web.dev-Performance-Docs erklären klar, wie preload Fonts früher discoverbar macht – wichtig ist dabei, dass Font-Requests korrekt (CORS) behandelt werden.

3) preconnect: wenn der Provider ein extra Roundtrip ist

Externe Font-Provider können zusätzliche Verbindungsaufbauten erzeugen. Best Practices: preconnect (inkl. crossorigin, wenn nötig), damit DNS/TLS nicht erst startet, wenn’s schon zu spät ist.

4) font-display: swap vs optional – keine Pauschalreligion

font-display steuert, wie der Browser mit Fallback/Swap umgeht. „swap“ sorgt für schnellen Text (gut für Perceived Performance), kann aber Layout verändern, wenn die Metriken abweichen. „optional“ kann Shifts reduzieren, riskiert aber, dass die Webfont gar nicht kommt (je nach Timing/Cache). Hier gilt: teste auf Mobile und guck auf CLS.

Quick Win Checkliste (Fonts)

  • Wie viele Font-Files lädst du wirklich? (Zählen. Nicht schätzen.)
  • Gibt’s preconnect zu externen Font-Hosts (inkl. crossorigin, wenn gebraucht)?
  • Ist preload nur für wenige Schlüssel-Fonts gesetzt (nicht für alles)?
  • Ist CLS auffällig beim ersten Render/Swap?
  • Ist Caching sauber (Fonts sollten lange cachbar sein, wenn versioniert)?

Scripts: INP retten ohne Großumbau

INP misst, wie schnell nach einer Interaktion die nächste visuelle Reaktion passiert. In der Praxis ist das häufig ein Main-Thread-Problem: zu viel Script-Evaluation, zu große Bundles, zu viele Third-Party-Tags, zu lange Tasks.

1) async vs defer: Basics, die immer noch falsch sind

  • defer: Lädt parallel, führt aber erst nach dem HTML-Parsing aus. Gut für die meisten „normalen“ Skripte.
  • async: Lädt parallel und kann sofort ausführen, sobald fertig. Gut für unabhängige Skripte, kann aber Timing/Order verändern.

Wenn du „alles async“ machst, kann das zu Chaos führen. Wenn du nichts davon nutzt, blockierst du unnötig. Ziel: kritischen Pfad entlasten, ohne Funktion zu brechen.

2) Bundle-Diät ohne Rewrite: Splitten, später laden, weniger initiale Arbeit

web.dev beschreibt bei „script evaluation and long tasks“ ziemlich direkt: reduziere initiale Bundle-Größe und nutze dynamische Imports, um Work zu verschieben. Das ist oft der größte Hebel, ohne deine App neu zu erfinden.

3) Long Tasks killen: „Yield often“ und Updates kleiner machen

Wenn Tasks länger als ~50ms laufen, blocken sie Interaktionen. In der Praxis sind das oft große Render-Updates, schwere Listener, oder Script-Bursts beim Start. Die „Top CWV“-Guides empfehlen explizit: öfter yielden, unnötiges JS vermeiden, große Rendering Updates reduzieren.

4) Third-Party Detox: der schnellste INP-Quick-Win

Third-Party JavaScript ist häufig der „unsichtbare Bossgegner“: Tag Manager, Consent, Analytics, Chat, Heatmaps, A/B-Testing, Widgets. web.dev hat dazu einen eigenen Guide: Third-Party möglichst aus dem kritischen Pfad halten und Risiken minimieren. Übersetzt: weniger, später, kontrollierter.

Quick Win Checkliste (Scripts)

  • Welche Skripte laufen vor der ersten Interaktion? (Insbesondere Third-Party)
  • Was ist wirklich kritisch, was kann später?
  • Nutzen „normale“ Skripte defer statt blocking?
  • Gibt es auffällige Long Tasks / hohe Script-Evaluation?
  • Können große Features per dynamic import nachgeladen werden?

Vergleich: Quick Wins nach Aufwand und Risiko

Hebel Typisches Ziel Aufwand Risiko Wenn du nur 1 Sache machst…
Hero/LCP Bild fixen LCP niedrig–mittel niedrig Hero nicht lazy + sinnvoll priorisieren
Responsive Images sauber LCP + Overall Load mittel mittel Mobile-Varianten richtig ausliefern
Fonts ausmisten + preload gezielt LCP/CLS niedrig–mittel mittel Nur Schlüssel-Fonts früh, Rest später
Third-Party reduzieren INP + LCP mittel mittel–hoch Inventur + „später laden“ Regeln
Bundle splitten (dynamic import) INP mittel mittel Nur initial laden, was sofort gebraucht wird

So setzt du’s um: 90-Minuten-Plan (ohne Rebuild)

Phase 1: Diagnose (20 Min)

  1. PSI auf 1–2 repräsentative Seiten (Artikel/Produkt/Startseite).
  2. LCP-Element identifizieren.
  3. Hinweise checken: „render blocking“, „unused JS“, „properly size images“, „serve modern formats“.

Phase 2: Quick Fix (50 Min)

  1. Bilder: Hero korrekt priorisieren; below-the-fold lazy; offensichtliche Overkill-Dateigrößen reduzieren.
  2. Fonts: unnötige Schnitte raus; preconnect/preload nur für Schlüssel; CLS im Blick.
  3. Scripts: blocking scripts in defer; Third-Party verschieben; offensichtliche „immer laden“ Tags killen.

Phase 3: Verifizieren (20 Min)

  1. Lighthouse erneut (gleiche Bedingungen) und vergleichen.
  2. Wenn möglich: auf echtem Mobile kurz testen (Scroll, Klick, Input).
  3. Field-Verlauf beobachten (Search Console/CrUX) – nicht nach 10 Minuten nervös werden.

Typische Fehler & wie du sie vermeidest

  • Fehler: „Wir preloaden jetzt ALLES.“ Fix: Preload nur für wenige, high-value Assets (Hero/Key Fonts) – sonst konkurrierst du mit dir selbst.
  • Fehler: Hero-Bild ist lazy „weil Best Practice“. Fix: Lazy nur below-the-fold, Hero priorisieren.
  • Fehler: Fonts swapen und CLS ignorieren. Fix: Fallback-Metriken und Layout-Stabilität testen, nicht nur „sieht hübsch aus“.
  • Fehler: Third-Party ist „nicht unser Problem“. Fix: Inventur + Regeln: was darf rein, wann lädt’s, wer ist Owner.
  • Fehler: Nur Lab messen. Fix: Field als Ziel, Lab als Werkzeug.

FAQ

Was ist der schnellste Hebel, wenn ich nur 1 Stunde Zeit habe?

Meist: Hero/LCP-Bild korrekt priorisieren (nicht lazy, passende Größe) und Third-Party aus dem kritischen Pfad schieben. Das bringt oft sofort spürbare Verbesserungen.

Wenn das LCP-Bild zu spät entdeckt wird (z.B. durch späte Komponenten oder CSS backgrounds). Für responsive Images gibt’s spezielle Empfehlungen, damit der Browser früh die passende srcset-Variante wählen kann.

Fonts früh discovern (gezielt preload), Fallbacks wählen, die metrisch ähnlich sind, und nicht zu viele Schnitte laden. Und dann: auf Mobile testen, ob beim Swap Layout rutscht.

Für die meisten „normalen“ Skripte ist defer ein guter Standard, weil es die Ausführung ans Ende des HTML-Parsings schiebt. async ist eher für unabhängige Skripte gedacht, bei denen Reihenfolge egal ist.

Weil Lab-Tests nicht deine echte Nutzerwelt abbilden: andere Geräte, CPU-Last, Third-Party-Zustände, Interaktionsmuster. Field-Daten (CrUX/Search Console) zeigen, wie sich Interaktionen in echt anfühlen.

Mach’s transparent: Liste aller Tags, Owner, Zweck, Ladezeitpunkt. Dann Regeln: „kritischer Pfad bleibt sauber“, vieles erst nach Consent oder Interaktion. Das ist fairer als „alles raus“ und wirkt oft sofort.

Fazit: Du brauchst keinen Rebuild – du brauchst Disziplin

Performance-Tuning ohne Neuaufbau funktioniert, wenn du Assets als Produkt behandelst: weniger, früher für das Wichtige, später für den Rest. Bilder bringen LCP nach vorne, Fonts verhindern Layout-Geflacker, Scripts retten INP – und Third-Party entscheidet oft, ob du überhaupt gewinnen kannst.

Nächster Schritt: Picke dir eine Seitentype (z.B. Artikel oder Produkt), identifiziere das LCP-Element und die größten Script-Blocker, und setze genau drei Fixes um: Hero priorisieren, Fonts ausmisten, Third-Party verschieben. Dann messen. Dann erst weiter.