Vermeiden Sie große, komplexe Layouts und Layoutüberlastungen

Das Layout ist der Ort, an dem der Browser die geometrischen Informationen für Elemente ermittelt, d. h. ihre Größe und Position auf der Seite. Jedes Element enthält explizite oder implizite Größeninformationen basierend auf dem verwendeten CSS, dem Inhalt des Elements oder einem übergeordneten Element. In Chrome wird dieser Vorgang als Layout bezeichnet.

Im Layout ermittelt der Browser die geometrischen Informationen für Elemente: ihre Größe und Position auf der Seite. Jedes Element hat explizite oder implizite Informationen zur Größe, die auf dem verwendeten CSS, dem Inhalt des Elements oder einem übergeordneten Element basieren. In Chrome (und abgeleiteten Browsern wie Edge) und Safari wird dieser Vorgang als Layout bezeichnet. In Firefox heißt das Reflow, aber die Vorgehensweise ist im Grunde derselbe.

Ähnlich wie bei Stilberechnungen sind die unmittelbaren Bedenken bezüglich der Layoutkosten folgende:

  1. Die Anzahl der Elemente, die ein Layout erfordern. Dieser Wert ist ein Nebenprodukt der DOM-Größe der Seite.
  2. Die Komplexität dieser Layouts.

Zusammenfassung

  • Das Layout hat direkte Auswirkungen auf die Interaktionslatenz.
  • Das Layout bezieht sich normalerweise auf das gesamte Dokument.
  • Die Anzahl der DOM-Elemente wirkt sich auf die Leistung aus. Sie sollten das Layout nach Möglichkeit nicht auslösen.
  • Vermeiden Sie erzwungene synchrone Layouts und Layout-Überlastungen. Lesen Sie Stilwerte und nehmen Sie dann Stiländerungen vor.

Auswirkungen des Layouts auf die Interaktionslatenz

Wenn ein Nutzer mit der Seite interagiert, sollten diese Interaktionen so schnell wie möglich erfolgen. Die Zeit, die für den Abschluss einer Interaktion benötigt wird – die endet, wenn der Browser den nächsten Frame anzeigt, um das Ergebnis der Interaktion anzuzeigen – wird als Interaktionslatenz bezeichnet. Dies ist ein Aspekt der Seitenleistung, der mit dem Messwert Interaction to Next Paint erfasst wird.

Die Zeit, die der Browser benötigt, um als Reaktion auf eine Nutzerinteraktion den nächsten Frame zu präsentieren, wird als Darstellungsverzögerung der Interaktion bezeichnet. Das Ziel einer Interaktion besteht darin, visuelles Feedback zu geben, um den Nutzern zu signalisieren, dass etwas passiert ist. Visuelle Aktualisierungen können eine gewisse Layoutarbeit erfordern, um dieses Ziel zu erreichen.

Um den INP Ihrer Website so niedrig wie möglich zu halten, sollten Sie Layouts nach Möglichkeit vermeiden. Wenn es nicht möglich ist, das Layout vollständig zu vermeiden, ist es wichtig, diese Layoutarbeit so zu begrenzen, dass der Browser den nächsten Frame schnell anzeigen kann.

Vermeiden Sie nach Möglichkeit ein Layout.

Wenn Sie Stile ändern, prüft der Browser, ob für eine der Änderungen das Layout berechnet und der Renderbaum aktualisiert werden muss. Änderungen an geometrischen Eigenschaften wie Breite, Höhe, links oder oben erfordern ein Layout.

.box {
  width: 20px;
  height: 20px;
}

/**
  * Changing width and height
  * triggers layout.
  */

.box--expanded {
  width: 200px;
  height: 350px;
}

Das Layout bezieht sich fast immer auf das gesamte Dokument. Wenn Sie viele Elemente haben, dauert es lange, bis Sie die Positionen und Abmessungen aller Elemente ermittelt haben.

Wenn es nicht möglich ist, auf ein Layout zu verzichten, sollten Sie erneut die Chrome-Entwicklertools verwenden, um zu sehen, wie lange es noch dauert, und zu ermitteln, ob das Layout die Ursache für einen Engpass ist. Öffnen Sie zuerst die Entwicklertools, gehen Sie zum Tab „Zeitachse“, klicken Sie auf „Aufzeichnen“ und interagieren Sie mit Ihrer Website. Wenn Sie die Aufzeichnung beenden, sehen Sie eine Aufschlüsselung der Leistung Ihrer Website:

In den DevTools wird eine lange Zeit im Layout angezeigt.

Wenn wir uns den Trace im obigen Beispiel genauer ansehen, sehen wir, dass für jeden Frame mehr als 28 Millisekunden im Layout verbracht werden. Bei 16 Millisekunden, die wir für die Darstellung eines Frames in einer Animation haben, ist das viel zu hoch. Außerdem sehen Sie in den DevTools die Größe des Baums (in diesem Fall 1.618 Elemente) und wie viele Knoten ein Layout benötigten (in diesem Fall 5).

Generell gilt hier, dass Sie Layouts nach Möglichkeit vermeiden sollten. Das ist aber nicht immer möglich. Wenn Sie das Layout nicht vermeiden können, wissen Sie, dass die Kosten des Layouts in Beziehung zur Größe des DOMs stehen. Obwohl die Beziehung zwischen den beiden nicht eng gekoppelt ist, verursachen größere DOMs in der Regel höhere Layoutkosten.

Erzwungene synchrone Layouts vermeiden

Der Versand eines Frames zum Display erfolgt in dieser Reihenfolge:

Flexbox als Layout verwenden

Zuerst wird JavaScript ausgeführt, dann Stilberechnungen und dann das Layout. Es ist jedoch möglich, einen Browser dazu zu zwingen, das Layout früher mit JavaScript auszuführen. Dies wird als erzwungenes synchrones Layout bezeichnet.

Beachten Sie zuerst, dass beim Ausführen des JavaScripts alle alten Layoutwerte aus dem vorherigen Frame bekannt sind und abgefragt werden können. Wenn Sie beispielsweise die Höhe eines Elements (nennen wir es „box“) am Anfang des Frames ausgeben möchten, könnten Sie folgenden Code schreiben:

// Schedule our function to run at the start of the frame:
requestAnimationFrame(logBoxHeight);

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

Es kann zu Problemen kommen, wenn Sie den Stil der Box geändert haben, bevor Sie nach ihrer Höhe fragen:

function logBoxHeight () {
  box.classList.add('super-big');

  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

Um die Frage nach der Höhe zu beantworten, muss der Browser zuerst die Stiländerung aufgrund des Hinzufügens der Klasse super-big anwenden und dann das Layout ausführen. Nur dann kann die richtige Höhe zurückgegeben werden. Das ist unnötig und möglicherweise kostspielig.

Aus diesem Grund sollten Sie Stillesevorgänge immer in einem Batch ausführen und zuerst ausführen, damit der Browser die Layoutwerte des vorherigen Frames verwenden kann. Anschließend können Sie Schreibvorgänge ausführen:

Die korrekte Funktionsweise der obigen Funktion würde so aussehen:

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);

  box.classList.add('super-big');
}

In den meisten Fällen müssen Sie keine Stile anwenden und dann Werte abfragen. Die Werte des letzten Frames sollten ausreichen. Wenn Sie die Stilberechnungen und das Layout synchron und früher ausführen, als der Browser möchte, kann es zu Engpässen kommen. Das ist in der Regel nicht empfehlenswert.

Layout-Thrashing vermeiden

Es gibt eine Möglichkeit, erzwungene synchrone Layouts noch schlimmer zu machen: Viele davon in schneller Folge ausführen. Sehen Sie sich diesen Code an:

function resizeAllParagraphsToMatchBlockWidth () {
  // Puts the browser into a read-write-read-write cycle.
  for (let i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = `${box.offsetWidth}px`;
  }
}

In diesem Code wird eine Gruppe von Absätzen durchlaufen und die Breite jedes Absatzes wird so festgelegt, dass sie der Breite eines Elements namens „box“ entspricht. Sie sieht harmlos aus, aber das Problem ist, dass bei jeder Iteration der Schleife ein Stilwert (box.offsetWidth) gelesen und dann sofort die Breite eines Absatzes (paragraphs[i].style.width) aktualisiert wird. Bei der nächsten Iteration der Schleife muss der Browser berücksichtigen, dass die Stile seit der letzten Anforderung von offsetWidth (in der vorherigen Iteration) geändert wurden. Daher muss er die Stiländerungen anwenden und das Layout ausführen. Dies geschieht bei jedem einzelnen Durchlauf.

Die Lösung für dieses Beispiel besteht darin, die Werte noch einmal zu lesen und dann zu schreiben:

// Read.
const width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth () {
  for (let i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = `${width}px`;
  }
}

Wenn Sie für Sicherheit sorgen möchten, sollten Sie FastDOM verwenden. Damit werden Lese- und Schreibvorgänge automatisch für Sie gruppiert und Sie können verhindern, dass versehentlich erzwungene synchrone Layouts oder Layout-Thrashing ausgelöst werden.