Umfüllen, Neuzeichnen, Kompositieren: Was jede CSS-Änderung den Browser kostet
Erfahren Sie, wie HTML und CSS durch DOM, CSSOM, Layout, Zeichnen und Kompositing ablaufen, warum Breitenänderungen aufwändiger sind als Farbänderungen und wie Sie Layout-Probleme vermeiden können.
Quellen: Browser Rendering Pipeline Explained: From HTML to Pixels + Reflow vs Repaint
Eine einzige Zeile Markup wie ein Überschriftstext erscheint unbedeutend, doch der Browser führt eine lange Abfolge von Schritten aus, bevor daraus Pixel auf dem Bildschirm entstehen. Das Verständnis dieser Abfolge ermöglicht es, praktische Fragen zu beantworten: Warum das Anpassen der Größe eines Elements eine Seite verlangsamen kann, warum Animationen in der Regel mit transform geschrieben werden, warum eine Änderung Auswirkungen auf Dutzende anderer Elemente haben kann und warum intensive DOM-Manipulationen zu einer ruckelnden Benutzeroberfläche führen. Diese Anleitung geht Schritt für Schritt durch jede Phase des Rendering-Pipelines und nutzt dieses Modell, um den Unterschied zwischen Reflow und Repaint sowie die Gewohnheiten zu erläutern, die dafür sorgen, dass Frames schnell bleiben.
<h1>Hello World</h1>
Ein Überblick über das Pipeline
Im Kern ist das Rendering-Pipeline der Prozess, bei dem drei Arten von Eingaben – Markup, Styles und Scripts – in ein Bild umgewandelt werden, das angezeigt werden kann.
HTML + CSS + JavaScript
↓
Pixels
↓
Screen
Wenn man die Abfolge in ihre einzelnen Phasen aufteilt, sieht sie ungefähr so aus:
Parse HTML
↓
Build DOM
↓
Parse CSS
↓
Build CSSOM
↓
Create Render Tree
↓
Calculate Styles
↓
Layout / Reflow
↓
Paint / Repaint
↓
Composite
↓
Pixels on Screen
Wenn Menschen darüber sprechen, wie schnell eine Seite zu ihrem ersten sichtbaren Inhalt gelangt, bezeichnen sie diese Abfolge in der Regel als Kritischen Renderpfad. Denken Sie daran, dass es sich um ein Modell handelt und nicht um eine Spezifikation für einen bestimmten Browser. Chrome, Firefox und Safari implementieren die Phasen unterschiedlich, und in späteren Updates überspringen sie Phasen, sobald nachgewiesen werden kann, dass die Arbeit unnötig ist. Das Modell bleibt dennoch ausreichend genau, um über Leistung zu urteilen – was für die tägliche Arbeit am Frontend entscheidend ist.
Phase 1: Parsing von HTML in den DOM
Alles beginnt mit dem HTML-Dokument, das der Server zurückgibt – beispielsweise eine kleine Seite mit einem Überschriftsblock und einem Absatz:
<!DOCTYPE html>
<html>
<body>
<h1>Hello World</h1>
<p>Welcome to my website.</p>
</body>
</html>
Der Parser liest diese Markup-Struktur und erzeugt das Document Object Model, einen Knotenbaum, in dem jedes Element sowie jeder Textabschnitt einen bestimmten Platz hat. Der Baum für das obige Dokument sieht wie folgt aus:
HTML
<html>
│
└── body
│
├── h1
│ └── "Hello World"
│
└── p
└── "Welcome..."
Das DOM erfasst, was das Dokument enthält und wie es strukturiert ist. Es dient außerdem als Schnittstelle für Ihre Skripte; eine Anfrage wie die unten gezeigte durchläuft diesen Baum, um einen Knoten zu finden:
document.querySelector("h1");
Die Parsing-Vorgänge erfolgen schrittweise. Der Browser wartet nicht auf das gesamte Dateiinhalt, bevor er mit dem Erstellen der Knoten beginnt – deshalb kann eine lange Seite bereits rendern, während der Rest noch eingestellt wird.
Phase 2: Parsing von CSS in das CSSOM
Allein die Struktur sagt nichts über das Erscheinungsbild aus. Dafür benötigt der Browser Stylesheets, wie zum Beispiel diese beiden einfachen Regeln:
h1 {
color: blue;
font-size: 32px;
}
p {
color: gray;
}
Der CSS-Code wird in eine zweite Struktur aufgespalten, das CSS Object Model. Eine nützliche Art, die beiden Strukturen voneinander zu unterscheiden, ist die Frage, die jede von ihnen beantwortet:
DOM
↓
"What exists on the page?"
CSSOM
↓
"How should those things look?"
Beim Rendering werden beide Antworten benötigt. Deshalb sind Stylesheets auch render-blockierend.
Phase 3: Zusammenführung beider Strukturen in den Render-Tree
Sobald DOM und CSSOM verfügbar sind, fügt der Browser sie zu einem Render-Tree zusammen.
DOM
+
CSSOM
↓
Render Tree
Der Render-Tree enthält nur das, was tatsächlich dargestellt wird, zusammen mit den darauf anwendbaren Styles. Nehmen wir diesen Fragment:
<div>
<h1>Hello</h1>
<p>World</p>
</div>
Nach der Zusammenführung kann man sich das als einen Baum sichtbarer Boxen vorstellen, die ihre Style-Informationen enthalten:
Render Tree
div
│
├── h1
│ ├── font-size: 32px
│ └── color: blue
│
└── p
└── color: gray
Alles, was nicht am Rendering teilnimmt, wird weggelassen. Ein Element mit display: none hat überhaupt keine Eintragung, was sich von visibility: hidden unterscheidet: Ein verstecktes Element nimmt weiterhin Platz ein, bleibt daher im Baum und beteiligt sich weiterhin am Layout.
Phase 4: Berechnung der berechneten Styles
Anschließend ermittelt der Browser den endgültigen, berechneten Style für jedes Element, das dargestellt werden soll. Reale Stylesheets enthalten überschneidende Regeln wie diese:
.card {
width: 300px;
}
.card p {
font-size: 16px;
}
Für jedes Element muss der Engine nacheinander drei Fragen klären:
Which rules apply?
↓
Which rule wins?
↓
What is the final computed style?
Die Beantwortung dieser Fragen bedeutet die Anwendung aller CSS-Lösungsregeln:
- die Kaskade
- die Vererbung von Elternelementen
- die Selektorspezifität
- die Umwandlung in berechnete Werte
- Styles, die von Media Queries abhängen
Das Ergebnis ist eine pro Element gelöste Eigenschaftensammlung:
DOM + CSSOM
↓
Style Calculation
↓
Computed Styles
Phase 5: Layout, bei der die Geometrie festgelegt wird
Zu diesem Zeitpunkt weiß der Browser, was angezeigt werden soll und wie es aussehen sollte. Was er noch nicht weiß, ist, wo jedes Element platziert wird und wie groß es ist. Die Beantwortung dieser Fragen ist die Aufgabe des Layouts, der einen Ansichtsbereich in eine Reihe positionierter Kästchen umwandelt:
Viewport
┌────────────────────────────┐
│ │
│ Header │
│ │
├──────────────┬─────────────┤
│ Sidebar │ Content │
│ │ │
│ │ │
└──────────────┴─────────────┘
Während des Layouts berechnet der Engine für jedes Kästchen im Renderbaum:
- Breite und Höhe
- Position
- Ränder und Abstände
- die Beziehung jedes Kästchens zu seinen Nachbarn und Vorgängern
- die Gesamtgeometrie der Seite
MDN fasst diese Phase als Bestimmung der Größe und des Standorts jedes Knotens im Renderbaum zusammen. Da die Kästchen voneinander abhängen, handelt es sich beim Layout um ein Problem für den gesamten Baum und nicht nur für jedes einzelne Element.
Reflow: Layout erneut ausführen
Der erste Durchlauf zur Berechnung der Geometrie wird einfach als Layout bezeichnet. Jeder nachfolgende Durchlauf, der durch eine Änderung ausgelöst wird, wird in der Regel Reflow genannt; einige Teams verwenden für denselben Vorgang den umgangssprachlichen Begriff Relayout:
Layout
↓
Initial calculation
Reflow / ReLayout
↓
Subsequent layout calculation
Der Rest dieses Leitfadens verwendet den Begriff Reflow, da dies der Ausdruck ist, den man in den meisten Diskussionen zu Leistung und Tools findet.
Was einen Reflow auslöst
Nehmen wir zunächst ein Feld mit fester Breite:
.box {
width: 200px;
}
Nun ändert ein Skript diese Breite zur Laufzeit:
box.style.width = "500px";
Diese eine Zuweisung stellt eine Reihe von Fragen, die der Browser beantworten muss, bevor er den nächsten Frame zeichnen kann:
The box changed width.
What else is affected?
Does its parent change?
Do neighboring elements move?
Does the text wrap differently?
Does the height change?
Does the entire layout need recalculation?
Daher kann ein Teil oder der gesamte Seiteninhalt einen neuen Layout-Durchlauf benötigen, nach dem auch die anschließenden Schritte ausgeführt werden:
Change width
↓
Style recalculation
↓
Layout / Reflow
↓
Paint
↓
Composite
Eigenschaften, die die Geometrie beeinflussen
Jede Eigenschaft, die die Größe oder Position beeinflusst, kann zu Layout-Aufgaben führen. Die üblichen Verdächtigen sind:
width
height
margin
padding
top
left
right
bottom
font-size
Die Änderung einer dieser Eigenschaften kann das Element selbst verschieben oder seine Größe ändern und potenziell alles darum herum beeinflussen. Beispielsweise können beide folgenden Aktualisierungen den Browser dazu zwingen, das Layout erneut zu berechnen:
element.style.width = "500px";
element.style.marginLeft = "100px";
font-size gehört zur Liste, weil sich durch Änderungen der Textgröße die Zeilenhöhe und das Umbruchverhalten ändern, was wiederum die Höhe des enthaltenden Blocks und somit die Position aller nachfolgenden Elemente verändert.
Warum der Neuausrichtungsprozess aufwendig ist
Betrachten Sie eine typische Seitenstruktur mit mehreren Bereichen und verschachteltem Inhalt:
Page
│
├── Header
├── Navigation
├── Sidebar
├── Main Content
│ ├── Card
│ ├── Card
│ ├── Card
│ └── Table
└── Footer
Falls ein Element in der oberen Region höher wird, benötigen möglicherweise alle darunterliegenden Karten, Tabellen und Fußzeilen neue Koordinaten. Eine einzige geometrische Änderung kann sich auf einen großen Teil des Layouts auswirken:
One Geometry Change
↓
Layout Recalculation
↓
Many Elements Recalculated
↓
Paint
↓
Composite
Diese Ausbreitung macht unnötige Layout-Arbeiten kostspielig, insbesondere wenn sie während einer Animation oder eines Ziehvorgangs wiederholt wird.
Schritt 6: Zeichnen
Sobald drei Dinge geklärt sind, kann der Browser mit der Erzeugung von Pixeln beginnen:
What to display
+
Where to display it
+
How it should look
Paint wandelt die positionierten Boxen in Zeichnungsoperationen um und letztendlich in Pixel. Der gezeichnete Inhalt umfasst:
- Text
- Farben und Hintergründe
- Ränder
- Schatten
- Bilder
- jede andere sichtbare Verzierung
Layout
↓
Paint
↓
Pixels
MDN beschreibt das Zeichnen als den Schritt, bei dem die Boxen vom Layout in tatsächliche Pixel auf dem Bildschirm umgewandelt werden.
Außerhalb zeichnen: neue Pixel, gleiche Geometrie
Ein Außerhalb zeichnen ist erforderlich, wenn ein Element anders aussieht, aber genau denselben Platz einnimmt. Der klassische Fall ist die Änderung der Textfarbe:
color: red;
Eine neue Farbe hat keinerlei Auswirkung auf eines der folgenden:
Width
Height
Position
Layout relationships
Dadurch kann der Browser den Layout-Vorgang ganz überspringen und einen kürzeren Weg nutzen:
Style Recalculation
↓
Paint / Repaint
↓
Composite
Reflow versus Repaint nebeneinander
Dieser Unterschied ist das Herzstück der meisten Ratschläge zur Leistungsförderung bei der Darstellung, weshalb es sinnvoll ist, jede Seite genau zu beschreiben.
Reflow berechnet die Geometrie erneut
Beim Reflow muss die Größe oder Position mindestens eines Elements erneut berechnet werden. Das Festlegen einer Breite ist ein typisches Beispiel:
element.style.width = "500px";
Dann wird die gesamte Kette der Schritte ausgeführt:
Change
↓
Style
↓
Layout / Reflow
↓
Paint
↓
Composite
Repaint ändert nur das Erscheinungsbild
Beim Repaint sieht das Element anders aus, doch es ist keine Neuberechnung der Geometrie notwendig. Das Festlegen einer Farbe reicht aus, um dies auszulösen:
element.style.color = "red";
Der Layout-Vorgang fällt aus der Kette heraus:
Change
↓
Style
↓
Paint / Repaint
↓
Composite
Jeder Punkt wird auf eine Zeile reduziert:
RELayout / Reflow
→ Geometry changes
Repaint
→ Visual appearance changes
Eine Schaltfläche, die Breite oder Farbe ändert
Stellen Sie sich eine einfache Aktionsschaltfläche vor:
┌─────────────────────┐
│ Buy Now │
└─────────────────────┘
Nehmen wir an, ihre Breite ändert sich vom ersten Wert zum zweiten:
width: 200px;
width: 400px;
Die Schaltfläche nimmt nun doppelt so viel horizontalen Platz ein, was dazu führen kann, dass benachbarte Elemente beiseitegedrängt werden oder die Zeile umgebrochen wird. Das ist eine geometrische Änderung, weshalb der Browser neu formatiert und anschließend neu zeichnet:
Width changes
↓
Geometry changes
↓
Reflow
↓
Repaint
Nehmen wir nun an, nur die Hintergrundfarbe ändert sich:
background-color: blue;
Die Schaltfläche behält ihre genaue Größe und Position bei, nur die Pixel unterscheiden sich:
Color changes
↓
Geometry unchanged
↓
Repaint
Die Beziehung ist einseitig
Wenn sich die Layouts ändern, landen die Elemente an neuen Positionen, und für diese neuen Positionen sind neue Pixel erforderlich:
Layout changes
↓
Browser needs new pixels
↓
Paint
Kurz gesagt, auf ein Neuformatieren folgt in der Regel eine Neuzeichnung:
Reflow
↓
Usually followed by
↓
Repaint
Das Gegenteil gilt nicht. Eine Neuzeichnung kann auch ohne jegliche Layout-Arbeit stattfinden:
Repaint
↓
Does NOT necessarily require
↓
Reflow
Phase 7: Zusammenfügen von Ebenen in ein Frame
Moderne Engines zeichnen nicht immer die gesamte Seite als ein einzelnes, flaches Bild. Sie können den Inhalt in separate Ebenen aufteilen und diese in einem letzten Zusammenfügevorgang zusammenstellen:
Layer 1
+
Layer 2
+
Layer 3
↓
Composite
↓
Final Frame
Eine Seite kann beispielsweise eine Hintergrundebene, eine Karte auf einer eigenen Ebene sowie ein animiertes Element auf einer dritten Ebene haben:
┌──────────────────────┐
│ Background Layer │
│ │
│ ┌──────────────┐ │
│ │ Card Layer │ │
│ └──────────────┘ │
│ │
│ ┌──────────────┐ │
│ │ Animation │ │
│ └──────────────┘ │
└──────────────────────┘
Der Kompositor stapelt diese Ebenen, um jedes Frame zu erzeugen. Einige Elemente können in eigene Ebenen erhoben werden, und in geeigneten Fällen übertragen Browser die Zusammenfassung auf die GPU. Eine Änderung, die lediglich eine vorhandene Ebene verschiebt oder mischt, kann somit ohne Neuzeichnung ihres Inhalts angewendet werden.
Warum transform die Standardmethode für Bewegungen ist
Hier ist ein Hover-Effekt, der einen Kasten durch Animation von left verschiebt – ausgehend von null:
.box {
left: 0;
}
und endet 100 Pixel nach rechts:
.box:hover {
left: 100px;
}
Weil left zum Positionieren des Elements im Layout gehört, kann jeder Schritt der Übergangsanimation einen Layout-Neuberechnungsvorgang sowie eine Neuzeichnung erfordern. Derselbe visuelle Bewegungseffekt kann stattdessen mit einer Transformierung dargestellt werden:
.box {
transform: translateX(0);
}
.box:hover {
transform: translateX(100px);
}
Eine Transformierung verschiebt das bereits gezeichnete Element, ohne dessen Layout-Box zu verändern. Dadurch kann der Browser die gesamte Animation oft im Kompositor bearbeiten, ohne pro Frame einen Layout- oder Zeichenvorgang durchführen zu müssen. opacity weist ähnliche Eigenschaften auf und ist die übliche Wahl für Ausblendeffekte.
Betrachten Sie dies eher als starke Empfehlung denn als unumstößliche Regel. Ob eine Transformierung tatsächlich im Kompositor verbleibt, hängt vom Element, den darauf angewendeten anderen Stilen, der Zuweisung der Ebenen sowie vom jeweiligen Browsermotor ab.
Welche Phasen werden durch eine Änderung tatsächlich ausgelöst
Bei den meisten Stilaktualisierungen verteilt sich die Arbeit nach der Neuberechnung des Styles auf einen von zwei Pfaden:
Change
↓
Recalculate Style
↓
┌───────┴───────┐
↓ ↓
Layout No Layout
↓ │
Paint │
↓ ↓
└───────┬───────┘
↓
Composite
↓
Screen
Je weniger teure Phasen bei einer häufigen Aktualisierung betroffen sind, desto einfacher ist es, eine reibungslose Darstellung aufrechtzuerhalten. MDN beschreibt denselben Ablauf: Neuberechnung des Styles, anschließend Layout, dann Zeichnen sowie Komposition, wo immer dies anwendbar ist. Änderungen an transform oder opacity auf einer komponierten Schicht können sogar die Zeichnungsphase überspringen, was der günstigste Weg von allen ist.
Der Frame-Budget: etwa 16,67 ms
Die meisten Anzeigegeräte erneuern den Inhalt etwa 60 Mal pro Sekunde, was für jeden Frame einen festen Zeitraum zur Verfügung stellt:
1 second
÷
60 frames
≈
16.67ms per frame
Anders ausgedrückt: Um 60 FPS zu erreichen, hat der Browser pro Frame etwa diese Zeit zur Verfügung:
16.67ms
Dieser Budgetrahmen wird von allem genutzt, was vor dem Anzeigen des Frames stattfinden muss: Ihrem JavaScript, der Berechnung der Styles, der Layout-Verarbeitung, dem Zeichnen sowie den internen Aufgaben des Browsers. Wenn die Gesamtkosten diesen Rahmen überschreiten, wird der Frame weggelassen und der Benutzer bemerkt Verzögerungen:
16.67ms
↓
Exceeded
↓
Frame missed
↓
Jank
Der Effekt ist am deutlichsten bei Interaktionen, die kontinuierlich Frames erzeugen:
- Scrollen
- Animationen
- Ziehen und Ablegen
- Ändern der Größe von Fenstern oder Panels
- Dashboards, die viele Werte gleichzeitig aktualisieren
MDN betont denselben Punkt: Die Berechnung der Styles, die Layout-Verarbeitung und das Zeichnen müssen alle in den Frame-Budgetrahmen passen, damit die Animation flüssig aussieht.
Layout-Probleme: Lesevorgänge, die ein synchrones Layout erzwingen
Eine besonders häufige Art, den Frame-Budgetrahmen zu verschwenden, sieht im Code unbedenklich aus. Der folgende Codeausschnitt setzt eine Breite, liest eine Größe ein, setzt eine Höhe fest und liest erneut:
element.style.width = "500px";
console.log(element.offsetWidth);
element.style.height = "500px";
console.log(element.offsetHeight);
Das von ihm erzeugte Muster besteht aus einem Wechsel zwischen Schreib- und Lesevorgängen:
Write
↓
Read layout
↓
Write
↓
Read layout
Normalerweise verschiebt der Browser die Layout-Berechnung auf den Zeitpunkt, an dem er den nächsten Frame zeichnen will, sodass mehrere Stiländerungen in einem Durchlauf abgewickelt werden können. Eigenschaften wie offsetWidth und offsetHeight müssen jedoch aktuelle Werte zurückgeben. Das Lesen einer solchen Eigenschaft unmittelbar nach einem Schreibvorgang zwingt den Engine, die ausstehenden Stil- und Layout-Arbeiten synchron und sofort zu bearbeiten. Wenn man das wiederholt – oft innerhalb einer Schleife über viele Elemente – entsteht Layout-Thrashing: viele vollständige Layout-Durchläufe, obwohl eigentlich nur einer ausreichen würde.
Die Lösung besteht darin, die Operationen so zu gruppieren, dass alle Lesevorgänge zuerst und alle Schreibvorgänge danach stattfinden:
Bad:
Write
Read
Write
Read
Write
Read
Better:
Read
Read
Read
Write
Write
Write
Die Art und Weise, wie Sie den Code umstrukturieren, hängt von der Anwendung ab, doch das Prinzip bleibt immer dasselbe: Lassen Sie den Browser die Layout-Berechnung nicht häufiger durchführen, als nötig. Der Blog bietet einen genaueren Blick auf Layout-Probleme und Live-DOM-Kollektionen, falls Sie weitere Beispiele für erzwungene Layout-Überarbeitungen möchten.
Praktische Techniken für kostengünstigeres Rendering
Animeerung von Transformations- und Transparenzwerten
Anstatt ein Element mit einer Layout-Eigenschaft wie folgt zu verschieben:
left: 100px;
wählen Sie stattdessen eine Transformationsmethode, die zum Design passt:
transform: translateX(100px);
Zum Aus- und Einfärben eines Elements wechseln Sie zwischen diesen beiden Werten:
opacity: 0;
opacity: 1;
ist in der Regel eine bessere Option als die Animeerung von Eigenschaften, die das Layout beeinflussen.
Begrenzen Sie Änderungen am DOM auf ein Minimum
Jede DOM-Veränderung kann weitere Aufgaben für den Browser veranlassen. Das Hinzufügen von Knoten nacheinander in einer solchen Reihenfolge:
element.appendChild(...);
element.appendChild(...);
element.appendChild(...);
wird oft besser dadurch ersetzt, dass der neue Inhalt außerhalb des Dokuments erstellt wird (zum Beispiel als DocumentFragment) und anschließend einmal eingefügt wird. Je größer und komplexer das DOM ist, desto teurer sind jede vermeidbare Änderung.
Gruppierter Update-Vorgang anstelle von abwechselnden Updates
Code, der Updates mit Layout-Arbeiten abwechselt, ergibt letztendlich eine Reihenfolge wie diese:
Update
↓
Layout
↓
Update
↓
Layout
↓
Update
↓
Layout
Das Ziel ist es, dem Browser eine Gruppe von Änderungen zu geben, die er gemeinsam verarbeiten kann:
Update
Update
Update
↓
Browser processes the changes
Für Animationen und andere visuelle Aufgaben hoher Frequenz legt requestAnimationFrame() Ihre Updates so fest, dass sie unmittelbar vor der Darstellung des nächsten Frames vom Browser ausgeführt werden, wodurch sie mit dem Renderzyklus übereinstimmen anstatt dagegenzuwirken.
Animieren Sie Layout-Eigenschaften nicht
Die Eigenschaften, die die Geometrie verändern, eignen sich nicht für Abläufe, die in jedem Frame ausgeführt werden:
width
height
margin
padding
top
left
right
bottom
font-size
Jede von ihnen kann das Layout auslösen. Wenn das visuelle Ergebnis es zulässt, animieren Sie stattdessen diese Eigenschaften:
transform
opacity
Reservieren Sie Platz für Bilder
Ein Bild-Tag ohne Abmessungen gibt dem Browser keine Hinweise darauf, wie viel Platz freigelassen werden soll:
<img src="image.jpg">
Sobald die Datei ankommt und ihre tatsächliche Größe bekannt ist, verschiebt sich der umgebende Inhalt und das Layout wird erneut berechnet. Durch Angabe der intrinsischen Größe im Voraus lässt sich das vermeiden:
<img
src="image.jpg"
width="800"
height="600"
alt="Example"
/>
Ein CSS aspect-ratio erzielt denselben Effekt für responsives Design. Auf jeden Fall kann der Browser den richtigen Rahmen vor dem Laden des Bildes reservieren, wodurch eine sichtbare Verschiebung des Layouts verhindert wird.
Messen Sie vor der Optimierung
Nicht jede Neuzeichnung oder Layout-Verarbeitungsschleife ist ein Problem, das es zu lösen gilt; Engines sind stark optimiert und erledigen den größten Teil dieser Arbeit schnell. Stützen Sie Ihre Bemühungen auf Belege mithilfe einer einfachen Schleife:
Performance Problem
↓
Measure
↓
Find Bottleneck
↓
Optimize
↓
Measure Again
Das Performance-Panel in Chrome DevTools zeichnet eine Zeitachse auf, die Folgendes anzeigt:
- Ausführung von JavaScript
- Neuberechnung der Styles
- Layout-Verarbeitung
- Neuzeichnung
- Kompositierungsarbeiten
- Lange Aufgaben
- Zeitplanung pro Frame
Beseitigen Sie das Engpassproblem, das die Analyse tatsächlich aufzeigt – nicht den Code, der beim Lesen nur teuer erscheint.
Der gesamte Prozess in einem Bild
Wenn alles von Anfang bis Ende zusammengeführt wird, sieht der Weg vom Quellcode bis zum Bildschirm so aus:
HTML
│
↓
Parse HTML
│
↓
DOM
│
│
CSS ──→ Parse CSS ──> CSSOM
│
↓
DOM + CSSOM
│
↓
Render Tree
│
↓
Style Calculation
│
↓
Layout / Reflow
│
↓
Paint / Repaint
│
↓
Composite
│
↓
Pixels on Screen
Und wenn sich nach der ersten Darstellung etwas ändert, ist die entscheidende Frage, ob die Geometrie betroffen ist:
CSS / DOM Change
↓
Style Recalculation
↓
Does geometry change?
│
┌──┴──┐
YES NO
│ │
↓ ↓
Layout Paint
│ │
└──┬───┘
↓
Composite
↓
Screen
Wichtige Erkenntnisse
Drei kurze Zusammenfassungen fassen das Wesentliche zusammen. Der Neuaufbau ist der teure Prozess:
RELayout / Reflow
↓
Changes geometry
↓
Width / Height / Position / Layout
↓
Usually leads to Paint
Das Neuzeichnen ist der günstigere Prozess:
Repaint
↓
Changes appearance
↓
Color / Background / Shadow / etc.
↓
Doesn't necessarily require Layout
Bei Animationen bestimmt die Wahl der Eigenschaft im Wesentlichen den Aufwand:
Layout properties
↓
Potentially expensive
transform + opacity
↓
Often better for smooth animations
Man muss die Interna jeder Engine nicht kennen, sollte aber in der Lage sein, den allgemeinen Ablauf nachzuvollziehen:
HTML
↓
DOM
CSS
↓
CSSOM
DOM + CSSOM
↓
Render Tree
↓
Style
↓
Layout
↓
Paint
↓
Composite
↓
Pixels
Unter Berücksichtigung dieses Modells:
- Geometrieveränderungen wie
widthlösen zunächst einen Neuaufbau und anschließend ein Neuzeichnen aus; Erscheinungsbildveränderungen wiecolorerfordern in der Regel nur ein Neuzeichnen. transformundopacitykönnen oft den Layout- und Zeichnungsprozess überspringen, weshalb sie sich für Animationen eignen.- Das Abwechseln von DOM-Schreibvorgängen mit Layout-Lesevorgängen führt zu Leistungsproblemen; Lesvorgänge sollten vor Schreibvorgängen erfolgen.
Der Browser erledigt stets die eigentliche Arbeit, um Code in Pixel umzuwandeln. Das Ziel ist es nicht, diese Arbeit zu beseitigen, sondern sicherzustellen, dass nicht mehr davon verlangt wird, als die Seite tatsächlich benötigt.