Laden von Ressourcen optimieren

Im vorherigen Modul wurde eine Theorie hinter dem kritischen Rendering-Pfad untersucht. Außerdem wurde untersucht, wie Ressourcen, die das Rendering und den Parser blockieren, das anfängliche Rendering einer Seite verzögern können. Nachdem Sie nun einige der Theorie dahinter verstanden haben, sind Sie in der Lage, einige Techniken zur Optimierung des kritischen Rendering-Pfads kennenzulernen.

Wenn eine Seite geladen wird, wird im HTML-Code auf viele Ressourcen verwiesen, die eine Seite mit ihrem Erscheinungsbild und Layout über CSS sowie ihre Interaktivität über JavaScript bereitstellen. In diesem Modul werden verschiedene wichtige Konzepte im Zusammenhang mit diesen Ressourcen und deren Auswirkungen auf die Ladezeit einer Seite behandelt.

Blockierung des Renderings

Wie im vorherigen Modul erläutert, ist CSS eine Ressource, die das Rendering blockiert, da sie den Browser daran hindert, Inhalte zu rendern, bis das CSS Object Model (CSSOM) erstellt wurde. Der Browser blockiert das Rendering, um ein Flash of Unstyled Content (FOUC) zu verhindern, was aus Sicht der Nutzerfreundlichkeit unerwünscht ist.

Im vorherigen Video sehen Sie eine kurze Übersicht, bei der Sie die Seite ohne Stil sehen können. Danach werden alle Stile angewendet, sobald der CSS-Code der Seite aus dem Netzwerk geladen wurde. Die Version der Seite ohne Stil wird sofort durch die Version mit benutzerdefinierten Stilen ersetzt.

Im Allgemeinen ist ein FOUC etwas, das Sie normalerweise nicht sehen. Es ist jedoch wichtig zu verstehen, warum der Browser das Rendern der Seite blockiert, bis CSS heruntergeladen und auf die Seite angewendet wurde. Das Blockieren des Renderings ist nicht unbedingt unerwünscht. Allerdings sollten Sie dafür sorgen, dass Ihr CSS-Code optimiert bleibt, um die Dauer der Anzeige so gering wie möglich zu halten.

Parserblockierung

Der HTML-Parser wird durch eine Parser-blockierende Ressource unterbrochen, z. B. ein <script>-Element ohne async- oder defer-Attribute. Wenn der Parser auf ein <script>-Element stößt, muss der Browser das Skript auswerten und ausführen, bevor der Rest des HTML-Codes geparst werden kann. Dies ist so konzipiert, da Skripts während der Erstellung das DOM während der Zeit ändern oder darauf zugreifen können.

<!-- This is a parser-blocking script: -->
<script src="/script.js"></script>

Bei Verwendung externer JavaScript-Dateien (ohne async oder defer) wird der Parser vom Erkennen der Datei bis zum Herunterladen, Parsen und Ausführen blockiert. Bei Verwendung von Inline-JavaScript wird der Parser ähnlich blockiert, bis das Inline-Skript geparst und ausgeführt wird.

Der Vorabladescanner

Der Preload Scanner ist eine Browseroptimierung in Form eines sekundären HTML-Parsers, der die HTML-Rohantwort scannt, um Ressourcen zu finden und spekulativ abzurufen, bevor der primäre HTML-Parser sie andernfalls entdecken würde. Mit dem Preload-Scanner kann der Browser beispielsweise mit dem Herunterladen einer in einem <img>-Element angegebenen Ressource beginnen, auch wenn der HTML-Parser beim Abrufen und Verarbeiten von Ressourcen wie CSS und JavaScript blockiert ist.

Damit Sie den Preload-Scanner nutzen können, sollten kritische Ressourcen in HTML-Markup eingefügt werden, das vom Server gesendet wird. Die folgenden Ressourcenlademuster können vom Vorabladescanner nicht erkannt werden:

  • Bilder, die von CSS mithilfe der Eigenschaft background-image geladen werden Diese Bildverweise befinden sich in CSS und können vom Vorabladescanner nicht ermittelt werden.
  • Dynamisch geladene Skripts in Form von <script>-Element-Markup, das mit JavaScript oder Modulen, die mit dem dynamischen import() geladen werden, in das DOM eingeschleust werden.
  • HTML, das auf dem Client mit JavaScript gerendert wird. Ein solches Markup ist in Strings in JavaScript-Ressourcen enthalten und kann vom Vorabladescanner nicht gefunden werden.
  • CSS-@import-Deklarationen

Diese Lademuster für Ressourcen sind alle spät erkannte Ressourcen und profitieren daher nicht vom Vorabladen-Scanner. Vermeiden Sie solche Anzeigen nach Möglichkeit. Wenn solche Muster nicht vermieden werden können, können Sie eventuell einen preload-Hinweis verwenden, um Verzögerungen bei der Ressourcenerkennung zu vermeiden.

CSS

CSS bestimmt die Darstellung und das Layout einer Seite. Wie bereits erwähnt, ist CSS eine Ressource, die das Rendering blockiert. Die Optimierung Ihres CSS-Codes kann sich also erheblich auf die Seitenladezeit insgesamt auswirken.

Reduzierung

Durch das Reduzieren von CSS-Dateien wird die Dateigröße einer CSS-Ressource reduziert, sodass sie schneller heruntergeladen werden können. Dies wird hauptsächlich dadurch erreicht, dass Inhalte wie Leerzeichen und andere unsichtbare Zeichen aus einer Quell-CSS-Datei entfernt werden und das Ergebnis in eine neu optimierte Datei ausgegeben wird:

/* Unminified CSS: */

/* Heading 1 */
h1 {
  font-size: 2em;
  color: #000000;
}

/* Heading 2 */
h2 {
  font-size: 1.5em;
  color: #000000;
}
/* Minified CSS: */
h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}

In ihrer einfachsten Form stellt die CSS-Komprimierung eine effektive Optimierung dar, mit der der FCP-Wert und in einigen Fällen sogar der LCP-Wert Ihrer Website verbessert werden kann. Tools wie Bundler können diese Optimierung in Produktions-Builds automatisch für Sie durchführen.

Nicht verwendete CSS entfernen

Vor dem Rendern von Inhalten muss der Browser alle Stylesheets herunterladen und parsen. Die für das Parsen benötigte Zeit umfasst auch Stile, die auf der aktuellen Seite nicht verwendet werden. Wenn Sie einen Bundler verwenden, der alle CSS-Ressourcen in einer einzigen Datei kombiniert, laden Ihre Nutzer wahrscheinlich mehr CSS herunter, als zum Rendern der aktuellen Seite erforderlich ist.

Verwenden Sie das Abdeckungstool in den Chrome-Entwicklertools, um nicht verwendeten CSS-Code für die aktuelle Seite zu ermitteln.

Screenshot des Tools „Abdeckung“ in den Chrome-Entwicklertools Im unteren Bereich ist eine CSS-Datei ausgewählt, in der eine erhebliche Menge an CSS angezeigt wird, die vom aktuellen Seitenlayout nicht verwendet wird.
Das Abdeckungstool in den Chrome-Entwicklertools ist nützlich, um CSS- und JavaScript-Code zu erkennen, der von der aktuellen Seite nicht verwendet wird. Damit können CSS-Dateien in mehrere Ressourcen aufgeteilt werden, die dann von verschiedenen Seiten geladen werden, anstatt ein viel größeres CSS-Bundle zu versenden, was das Rendern der Seite verzögern kann.

Das Entfernen von nicht verwendetem CSS hat zwei Auswirkungen: Die Downloadzeit wird reduziert und die Konstruktion des Renderingbaums optimiert, da der Browser weniger CSS-Regeln verarbeiten muss.

CSS-@import-Deklarationen vermeiden

Auch wenn dies praktisch erscheint, sollten Sie @import-Deklarationen in CSS vermeiden:

/* Don't do this: */
@import url('style.css');

Ähnlich wie das <link>-Element in HTML können Sie mit der @import-Deklaration in CSS eine externe CSS-Ressource aus einem Stylesheet importieren. Der Hauptunterschied zwischen diesen beiden Ansätzen besteht darin, dass das HTML-<link>-Element Teil der HTML-Antwort ist und daher viel früher erkannt wird als eine CSS-Datei, die über eine @import-Deklaration heruntergeladen wird.

Der Grund dafür ist, dass die CSS-Datei, in der sie enthalten ist, zuerst heruntergeladen werden muss, damit eine @import-Deklaration erkannt wird. Dies führt zu einer sogenannten Anfragekette, die – im Fall von CSS – verzögert, wie lange das erste Rendern einer Seite dauert. Ein weiterer Nachteil besteht darin, dass Stylesheets, die mit einer @import-Deklaration geladen werden, vom Preload-Scanner nicht erkannt werden und deshalb zu spät erkannten Ressourcen werden, die das Rendering blockieren.

<!-- Do this instead: -->
<link rel="stylesheet" href="style.css">

In den meisten Fällen können Sie @import mit einem <link rel="stylesheet">-Element ersetzen. Mit <link>-Elementen können Stylesheets gleichzeitig heruntergeladen werden und die Gesamtladezeit wird reduziert, im Gegensatz zu @import-Deklarationen, bei denen Stylesheets direkt heruntergeladen werden.

Wichtige CSS-Elemente inline einbinden

Die zum Herunterladen von CSS-Dateien benötigte Zeit kann den FCP-Wert einer Seite erhöhen. Durch das Einfügen kritischer Stile im Dokument <head> wird die Netzwerkanfrage für eine CSS-Ressource eliminiert. Wenn das richtig gemacht wird, können die anfänglichen Ladezeiten verbessert werden, wenn der Browser-Cache eines Nutzers nicht vorbereitet ist. Der verbleibende CSS-Code kann asynchron geladen oder am Ende des <body>-Elements angehängt werden.

<head>
  <title>Page Title</title>
  <!-- ... -->
  <style>h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}</style>
</head>
<body>
  <!-- Other page markup... -->
  <link rel="stylesheet" href="non-critical.css">
</body>

Der Nachteil ist, dass durch das Einfügen einer großen Menge von CSS mehr Bytes zur ersten HTML-Antwort hinzugefügt werden. Da HTML-Ressourcen häufig nicht sehr lange (oder überhaupt) im Cache gespeichert werden können, wird der Inline-CSS-Code nicht für nachfolgende Seiten, die denselben CSS-Code in externen Stylesheets verwenden, im Cache gespeichert. Testen und messen Sie die Leistung Ihrer Seite, um sicherzustellen, dass sich die Vor- und Nachteile lohnen.

CSS-Demos

JavaScript

JavaScript fördert den Großteil der Interaktivität im Web, aber es hat seinen Preis. Wenn zu viel JavaScript versendet wird, kann deine Webseite beim Laden der Seite langsamer reagieren. Außerdem kann es zu Reaktionsproblemen kommen, die Interaktionen verlangsamen. Beides kann für Nutzer frustrierend sein.

JavaScript, das das Rendering blockiert

Wenn <script>-Elemente ohne die Attribute defer oder async geladen werden, blockiert der Browser das Parsen und Rendern, bis das Skript heruntergeladen, geparst und ausgeführt wird. In ähnlicher Weise blockieren Inline-Skripts den Parser, bis das Skript geparst und ausgeführt wurde.

async im Vergleich zu defer

async und defer ermöglichen das Laden externer Skripts, ohne den HTML-Parser zu blockieren. Skripts (einschließlich Inline-Skripts) mit type="module" werden automatisch zurückgestellt. Es gibt jedoch einige Unterschiede zwischen async und defer, die es zu beachten gilt.

Eine Darstellung verschiedener Mechanismen zum Laden von Skripts, die alle die Rollen für Parser, Abruf und Ausführung auf der Grundlage verschiedener verwendeten Attribute wie async, defer, type=&#39;module&#39; und eine Kombination aus allen drei enthalten.
Quelle: https://html.spec.whatwg.org/multipage/scripting.html

Mit async geladene Skripts werden sofort nach dem Download geparst und ausgeführt. Mit defer geladene Skripts werden hingegen ausgeführt, wenn das Parsen des HTML-Dokuments abgeschlossen ist. Dies erfolgt gleichzeitig mit dem DOMContentLoaded-Ereignis des Browsers. Außerdem können async-Skripts in falscher Reihenfolge ausgeführt werden, während defer-Skripts in der Reihenfolge ausgeführt werden, in der sie im Markup aufgeführt sind.

Clientseitiges Rendering

Generell sollte JavaScript nicht verwendet werden, um kritische Inhalte oder das LCP-Element einer Seite zu rendern. Dies wird als clientseitiges Rendering bezeichnet und ist eine Methode, die häufig in Single-Page-Anwendungen (SPAs) verwendet wird.

Das von JavaScript gerenderte Markup übergeht den Vorabladescanner, da die Ressourcen im client-gerenderten Markup nicht gefunden werden. Dies kann den Download wichtiger Ressourcen wie eines LCP-Bilds verzögern. Der Browser beginnt erst dann mit dem Herunterladen des LCP-Bilds, wenn das Skript ausgeführt und das Element dem DOM hinzugefügt wurde. Das Skript kann wiederum nur ausgeführt werden, nachdem es erkannt, heruntergeladen und geparst wurde. Dies wird als Kette kritischer Anfragen bezeichnet und sollte vermieden werden.

Außerdem werden beim Rendern von Markup mit JavaScript wahrscheinlich längere Aufgaben generiert als bei Markup, das als Antwort auf eine Navigationsanfrage vom Server heruntergeladen wird. Ein intensiver Einsatz des clientseitigen HTML-Renderings kann die Interaktionslatenz negativ beeinflussen. Dies gilt insbesondere in Fällen, in denen das DOM einer Seite sehr groß ist. Dies führt zu erheblichem Rendering, wenn JavaScript das DOM ändert.

Reduzierung

Ähnlich wie bei CSS reduziert das Reduzieren von JavaScript die Dateigröße einer Skriptressource. Dies kann zu schnelleren Downloads führen und dem Browser ermöglicht es, JavaScript schneller zu parsen und zu kompilieren.

Darüber hinaus geht die Komprimierung von JavaScript noch einen Schritt weiter als die Komprimierung anderer Assets wie CSS. Wenn JavaScript reduziert wird, werden nicht nur Leerzeichen, Tabulatoren und Kommentare angezeigt, sondern Symbole im Quell-JavaScript werden gekürzt. Dieser Vorgang wird auch als Uglifizierung bezeichnet. Sehen Sie sich den folgenden JavaScript-Quellcode an:

// Unuglified JavaScript source code:
export function injectScript () {
  const scriptElement = document.createElement('script');
  scriptElement.src = '/js/scripts.js';
  scriptElement.type = 'module';

  document.body.appendChild(scriptElement);
}

Wenn der vorherige JavaScript-Quellcode nicht korrekt ist, kann das Ergebnis in etwa wie das folgende Code-Snippet aussehen:

// Uglified JavaScript production code:
export function injectScript(){const t=document.createElement("script");t.src="/js/scripts.js",t.type="module",document.body.appendChild(t)}

Im vorherigen Snippet sehen Sie, dass die menschenlesbare Variable scriptElement in der Quelle auf t gekürzt wurde. Bei Anwendung auf eine große Sammlung von Skripts können die Einsparungen beträchtlich sein, ohne die Funktionen des Produktions-JavaScripts einer Website zu beeinträchtigen.

Wenn Sie einen Bundler zur Verarbeitung des Quellcodes Ihrer Website verwenden, erfolgt die Uglifizierung häufig automatisch für Produktions-Builds. Uglifier-Methoden wie Terser sind ebenfalls hoch konfigurierbar, sodass Sie die Aggressivität des Uglification-Algorithmus anpassen können, um maximale Einsparungen zu erzielen. Die Standardeinstellungen eines Tools zur Uglifizierung reichen jedoch in der Regel aus, um ein ausgewogenes Verhältnis zwischen Ausgabegröße und Beibehaltung der Funktionen zu erreichen.

JavaScript-Demos

Wissen testen

Wie werden mehrere CSS-Dateien am besten in den Browser geladen?

Die CSS-Deklaration @import
Versuche es bitte noch einmal.
Mehrere <link>-Elemente.
Richtig!

Was bewirkt der Vorabladescanner des Browsers?

Er ist ein sekundärer HTML-Parser, der Roh-Markup prüft, um Ressourcen zu finden, bevor der DOM-Parser es tun kann, um sie schneller zu finden.
Richtig!
Erkennt <link rel="preload">-Elemente in einer HTML-Ressource.
Versuche es bitte noch einmal.

Warum blockiert der Browser beim Herunterladen von JavaScript-Ressourcen vorübergehend standardmäßig das Parsen von HTML?

Um das Flash of Unstyled Content (FOUC) zu verhindern.
Versuche es bitte noch einmal.
Weil die Auswertung von JavaScript eine sehr CPU-intensive Aufgabe ist und das Pausieren des HTML-Parsings der CPU mehr Bandbreite zur Verfügung stellt, um das Laden der Skripts abzuschließen.
Versuche es bitte noch einmal.
Weil Skripts das DOM ändern oder auf andere Weise darauf zugreifen können.
Richtig!

Nächster Schritt: Browser mit Ressourcenhinweisen unterstützen

Sie wissen jetzt, wie sich die im <head>-Element geladenen Ressourcen auf den ersten Seitenaufbau und verschiedene Messwerte auswirken können. Als Nächstes wird es Zeit für den nächsten Schritt. Im nächsten Modul befassen wir uns mit Ressourcenhinweisen und wie sie dem Browser wertvolle Hinweise geben können, damit er Ressourcen schneller laden und Verbindungen zu ursprungsübergreifenden Servern schneller öffnen kann, als der Browser sonst ohne sie tun würde.