Notizbuch · Notiz 001 · Web-Performance
Wo sich auf einer statischen Website die letzten 18 Punkte versteckten
Bilder komprimiert, JavaScript in drei Dateien, kein Framework. Lighthouse zeigte trotzdem 82. Die fehlenden Punkte steckten an drei Stellen, die das Tool zwar benannte, aber nicht erklärte.
Gründer, BLN Global · · 7 Min. Lesezeit
Kurze Antwort
Bilder und JavaScript waren bereits gut. Die restlichen Punkte steckten an drei Stellen. Erstens: Die Prüfung, die unnötige Arbeit vermeiden sollte, las selbst das Layout aus und zwang den Browser zum Rechnen. Zweitens: Das Stylesheet und externe Schriftarten verzögerten die erste Darstellung um 600 ms. Wir haben das CSS in die Seite eingebettet und die Schriften auf unseren Server gelegt. Drittens: Von 24 Kontrastwarnungen der Barrierefreiheitsprüfung waren 6 Fehlalarme. Der vollständige Scan fand danach 13 Seiten, die in der Stichprobe fehlten.
Auf dieser Website läuft kein Framework. Python-Skripte erzeugen statisches HTML für die Seiten. Die Styles stehen in einer CSS-Datei, JavaScript in drei kleinen Dateien. Die Bilder liegen als WebP in drei Größen vor. Für die Performance hatten wir die offensichtlichen Punkte also schon erledigt.
Der Mobile-Score blieb bei 82.
Nach den offensichtlichen Fehlern fehlten die restlichen Punkte nicht wegen eines großen Problems. Sie steckten an drei verschiedenen Stellen. Alle zu finden hieß weniger, die Warnung des Tools zu lesen, als die Warnung was du meinst du musstest es verstehen.
Platz eins: die Prüfung, die Arbeit überspringen sollte
Lighthouse warnt mit „erzwungener Reflow“, wenn JavaScript den Browser zwingt, das Seitenlayout zu früh zu berechnen. Der Browser sammelt Stiländerungen und wendet sie auf einmal an. Liest das Skript dazwischen einen Messwert aus, muss der Browser die gesammelten Änderungen sofort verarbeiten.
Die Warnung zeigte auf die Quellzeile, doch dort war keine sichtbare Messung. Nach drei Durchgängen war die Ursache klar.
- Im ersten Durchlauf
getComputedStyleAufrufe entfernt. Der Score blieb gleich. - In der zweiten Runde zeigte sich der eigentliche Täter: ein Skript, das unnötige Arbeit überspringen sollte
if (window.scrollY > 0)Prüfung. Die Prüfung selbst liest das Layout aus. Die Zeile, die Arbeit vermeiden sollte, löste genau diese Arbeit aus. - Im dritten Durchgang flog die Funktion komplett raus. CSS definierte den Ausgangszustand bereits. JavaScript musste ihn nicht noch einmal setzen.
Ein Muster bleibt noch void element.offsetWidth war. Ein gängiger, akzeptierter Trick: das Layout bewusst erzwingen, damit eine Transition neu startet. Stattdessen zwei requestAnimationFrame kam dazu. Erfüllt denselben Zweck, ohne das Layout zu erzwingen.
function yenidenBaslat(fn) { /* Läuft der Tab im Hintergrund, startet rAF nicht und die Demo bleibt leer. */ if (document.hidden) { fn(); return; } requestAnimationFrame(function () { requestAnimationFrame(fn); }); }
Die Erkenntnis: Eine Performancewarnung sagt meist nicht „Du machst zu viel“, sondern „Du machst es zum falschen Zeitpunkt“.
Platz zwei: die 600 ms vor dem ersten Paint
Die zweite Warnung betraf Ressourcen, die den ersten Bildaufbau blockierten. Der Browser zeichnete nichts, bevor er das Stylesheet und danach die Schriften geladen und verarbeitet hatte. Die gemessene Verzögerung lag bei 600 Millisekunden.
Stylesheet steckt direkt in der Seite
Eine eigene CSS-Datei kommt beim zweiten Besuch aus dem Cache. Das hilft. Beim ersten Besuch kostet sie aber einen zusätzlichen Roundtrip, und genau diesen ersten Besuch erlebt jemand aus der Suche. Beim Deploy verkleinern wir CSS und betten es in die Seite ein. Wir haben gemessen: Nach Brotli war die eingebettete Variante kleiner als separate Datei plus zweiter Request.
Fonts liegen auf unserem Server
Das Setup von Google Fonts verband sich mit zwei getrennten Origins: für Styles mit fonts.googleapis.com, für Dateien fonts.gstatic.com. Jede neue Quelle braucht eine DNS-Auflösung, einen TLS-Handshake und eine neue Verbindung. Ohne diese Anfragen messen wir folgendes Ergebnis:
Die Schriftdateien einer türkischen Seite. 3 Dateien aus einer Quelle statt 6 Dateien und 2 externen Quellen.
unicode-range über die Felder haben wir die Subsets getrennt, die eine türkische Seite wirklich lädt (latin + latin-ext), und jede Dateigröße einzeln gemessen. Die andere Seite in diesem Repo assets/fonts echte Größe der Dateien.Wie die Schriftdateien kleiner wurden und was die Erweiterung auf zehn Sprachen kostete, steht in einer eigenen Notiz: zehn Sprachen, drei Schriftdateien.
Dritte Stelle: hier liegt das Tool falsch
Die Barrierefreiheitsprüfung meldete 24 Kontrastprobleme. Hätten wir alle ungeprüft korrigiert, wäre die Hälfte des Designs kaputtgegangen. 6 Meldungen waren falsch.
Grund content-visibility:auto war. Diese Funktion verarbeitet Bereiche außerhalb des Bildschirms erst später und beschleunigt so das erste Rendering. Genau das wollten wir. Doch die berechnete Farbe des zurückgestellten Bereichs erreichte das Prüfwerkzeug nicht. Deshalb meldete es Weiß auf Weiß. Auf dem Bildschirm gab es dieses Problem nicht.
Wir haben die echten Verhältnisse einzeln im Browser gemessen. 18 Warnungen stimmten und wurden korrigiert. 6 lagen am blinden Fleck des Tools: content-visibility Wir haben es nur aus den zwei problematischen Abschnitten entfernt. In den übrigen blieb es stehen.
Die Warnung eines Audit-Tools beweist keinen Fehler. Sie ist eine Behauptung. Wir prüfen sie, dann beheben wir den Fehler. Diese Lektion tauchte auf dieser Seite noch zweimal auf. Einmal bei in der Notiz zur Sprachprüfung steht dort.
Dazu kommt unser eigener Fehler
Damit das Layout nicht springt, brauchen alle Bilder width und height Das Attribut kam dazu. Richtiger Schritt. Der Browser reserviert den Platz für das Bild, bevor er es lädt. Aber die Demo-Boxen brachen: Einige Bilder erschienen 58 Pixel breit und 1500 Pixel hoch.
Der Grund: im HTML width und height Attribute geben Darstellungshinweise, und die Werte im CSS height sie füllen den Wert aus. Im Design gibt es für diese Felder nur aspect-ratio war definiert, height war leer. Das Attribut landete dort und drückte die Rate. Korrektur in einer Zeile:
img{max-width:100%;height:auto;display:block}
Und hier bringt der Scan wirklich etwas
Als die Startseite 100 hatte, wirkte die Sache erledigt. War sie nicht. Wir hatten nur eine Stichprobe gemessen. Der Website alle 150 Seiten liefen einzeln durch Lighthouse. Das Ergebnis: 137 Seiten erreichten in allen vier Kategorien 100, nicht 13 Seiten. Barrierefreiheit, Best Practices und SEO lagen auf jeder Seite bei 100. Nur die Performance wich ab.
Elf von dreizehn Seiten hatten dieselbe Sprache: Arabisch. Der niedrigste Wert: 83.
/ar/safecoast/ 83 /ar/laya/ 84 /ar/localmind/ 84 /ar/adforge/ 85 /ar/garanti/ 85 /ar/gtin/ 86 /ar/visionguard/ 86 /ar/ 89 /ar/growthos/ 97 /ar/dream/ 99 /ar/twin/ 99
Lighthouse nannte den Grund direkt: Webfont geladen → ar.woff2. Die arabische Schrift kam zu spät, der Browser setzte die Seite neu. Die arabische Ersatzschrift maß ganz anders als Noto Sans Arabic, deshalb fiel der Sprung groß aus. Für die Korrektur trafen wir dieselbe Entscheidung wie schon bei Martian Mono: swap nicht optional.
Layout-Verschiebungen (CLS) auf arabischen Seiten
Die eigentliche Lektion betrifft nicht die Performance. Wenn du nur Stichproben prüfst und der Website 100 Punkte gibst, behauptest du etwas über Seiten, die du nicht gemessen hast. Fünfzehn Seiten blieben deshalb monatelang bei 83 Punkten. Wir hatten sie nie geprüft.
Grenze des Ergebnisses
Der Punktwert stammt aus einem Labortest. Er setzt ein festes Gerät und ein festes Netzmodell voraus. Das Telefon und die Verbindung echter Besucher können schlechter sein. Mehrere Messungen derselben Seite schwanken zwischen 98 und 100. 100 Punkte bedeuten nicht, dass die Seite für alle schnell ist. Sie bedeuten, dass wir die messbaren Hindernisse entfernt haben. Sobald genug echte Nutzerdaten vorliegen, zählt Chromes Felddatensatz.
Diese drei Korrekturen hatten eins gemeinsam: Keine davon bedeutete mehr Arbeit. Alle drei haben Arbeit entfernt, die zum falschen Zeitpunkt lief oder gar nicht nötig war.