Laden von Ressourcen optimieren

Im vorherigen Modul haben wir uns mit der Theorie hinter dem kritischen Renderingpfad beschäftigt und damit, wie Ressourcen, die das Rendering und den Parser blockieren, das erste Rendering einer Seite's verzögern können. Nachdem Sie nun die Theorie dahinter kennen, können Sie einige Techniken zur Optimierung des kritischen Renderingpfads kennenlernen.

Beim Laden einer Seite werden im HTML-Code viele Ressourcen referenziert, die das Erscheinungsbild und das Layout der Seite über CSS sowie die Interaktivität über JavaScript bestimmen. In diesem Modul werden eine Reihe wichtiger Konzepte im Zusammenhang mit diesen Ressourcen und ihren Auswirkungen auf die Ladezeit einer Seite behandelt.

Rendering blockieren

Wie im vorherigen Modul erläutert, ist CSS eine Ressource, die das Rendering blockiert, da sie verhindert, dass der Browser Inhalte rendert, bis das CSS-Objektmodell (CSSOM) erstellt wurde. Der Browser blockiert das Rendering, um ein Flash of Unstyled Content (FOUC) zu verhindern, was aus Nutzersicht unerwünscht ist.

Im vorherigen Video sehen Sie kurz ein FOUC, bei dem die Seite ohne Styling angezeigt wird. Anschließend werden alle Stile angewendet, sobald das CSS der Seite aus dem Netzwerk geladen wurde. Die Version der Seite ohne Styling wird sofort durch die Version mit Styling ersetzt.

Im Allgemeinen sehen Sie ein FOUC nicht, aber es ist wichtig, das Konzept zu verstehen, damit Sie wissen, warum der Browser das Rendering der Seite blockiert, bis CSS heruntergeladen und auf die Seite angewendet wurde. Das Blockieren des Renderings ist nicht unbedingt unerwünscht, aber Sie sollten die Dauer minimieren, indem Sie Ihr CSS optimieren.

Parser blockieren

Eine Parser-blockierende Ressource unterbricht den HTML-Parser, z. B. ein <script> Element ohne die Attribute async oder defer. Wenn der Parser auf ein <script> Element stößt, muss der Browser das Skript auswerten und ausführen, bevor er mit dem Parsen des restlichen HTML-Codes fortfahren kann. Das ist so gewollt, da Skripts das DOM möglicherweise ändern oder darauf zugreifen, während es noch erstellt wird.

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

Bei Verwendung externer JavaScript-Dateien (ohne async oder defer oder type=module, das standardmäßig defer ist) wird der Parser blockiert, von dem Zeitpunkt an, an dem die Datei gefunden wird, bis sie heruntergeladen, geparst und ausgeführt wurde. Bei Verwendung von Inline-JavaScript wird der Parser ebenfalls blockiert, bis das Inline-Skript geparst und ausgeführt wurde.

Der Preload-Scanner

Der Preload-Scanner ist eine Browseroptimierung in Form eines sekundären HTML Parsers, der die Roh-HTML-Antwort scannt, um Ressourcen zu finden und spekulativ abzurufen, bevor der primäre HTML-Parser sie sonst finden würde. So kann der Browser beispielsweise mit dem Herunterladen einer Ressource beginnen, die in einem <img> Element angegeben ist, auch wenn der HTML-Parser blockiert ist, während er Ressourcen wie CSS und JavaScript abruft und verarbeitet.

Damit der Preload-Scanner genutzt werden kann, müssen kritische Ressourcen im HTML-Markup enthalten sein, das vom Server gesendet wird. Die folgenden Muster zum Laden von Ressourcen können vom Preload-Scanner nicht erkannt werden:

  • Bilder, die mit CSS über die Eigenschaft background-image geladen werden. Diese Bildreferenzen befinden sich in CSS und können vom Preload-Scanner nicht erkannt werden.
  • Dynamisch geladene Skripts in Form von <script> Element-Markup, das mit JavaScript in das DOM eingefügt wird, oder Module, die mit dynamischem import() geladen werden.
  • HTML, das clientseitig mit JavaScript gerendert wird. Solches Markup ist in Strings in JavaScript-Ressourcen enthalten und kann vom Preload-Scanner nicht erkannt werden.
  • CSS-Deklarationen @import.

Diese Muster zum Laden von Ressourcen sind alles Ressourcen, die erst spät erkannt werden, und profitieren daher nicht vom Preload-Scanner. Vermeiden Sie sie nach Möglichkeit. Wenn das nicht möglich ist, können Sie jedoch einen preload Hinweis verwenden, um Verzögerungen bei der Ressourcenerkennung zu vermeiden.

CSS

CSS bestimmt die Darstellung und das Layout einer Seite. Wie bereits beschrieben, ist CSS eine Ressource, die das Rendering blockiert. Die Optimierung Ihres CSS-Codes kann sich daher erheblich auf die gesamte Ladezeit der Seite auswirken.

Reduzierung

Durch das Reduzieren von CSS-Dateien wird die Dateigröße einer CSS-Ressource verringert, sodass sie schneller heruntergeladen werden kann. Dies wird hauptsächlich erreicht, indem Inhalte wie Leerzeichen und andere unsichtbare Zeichen aus einer CSS-Quelldatei entfernt 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 seiner einfachsten Form ist die CSS-Reduzierung eine effektive Optimierung, die den FCP Ihrer Website verbessern und in einigen Fällen sogar den LCP beeinflussen kann. Tools wie Bundler können diese Optimierung automatisch für Sie in Produktions Builds durchführen.

Nicht verwendete CSS entfernen

Bevor Inhalte gerendert werden können, müssen alle Stylesheets vom Browser heruntergeladen und geparst werden. Die Zeit, die für das Parsen benötigt wird, 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.

Wenn Sie nicht verwendetes CSS für die aktuelle Seite ermitteln möchten, verwenden Sie das Tool Codeabdeckung in den Chrome Entwicklertools.

Ein Screenshot des Tools „Abdeckung“ in den Chrome-Entwicklertools. Im unteren Bereich ist eine CSS-Datei ausgewählt, die eine beträchtliche Menge an CSS enthält, die vom aktuellen Seitenlayout nicht verwendet wird.
Das Tool „Codeabdeckung“ in den Chrome-Entwicklertools ist nützlich, um CSS (und JavaScript) zu erkennen, das auf der aktuellen Seite nicht verwendet wird. Es kann verwendet werden, um CSS-Dateien in mehrere Ressourcen aufzuteilen, die von verschiedenen Seiten geladen werden sollen, anstatt ein viel größeres CSS-Bundle zu verwenden, das das Rendering der Seite verzögern kann.

Das Entfernen von nicht verwendetem CSS hat einen doppelten Effekt: Neben der Reduzierung der Download zeit optimieren Sie auch die Erstellung des Renderbaums, da der Browser weniger CSS-Regeln verarbeiten muss.

CSS-Deklarationen @import vermeiden

Auch wenn es 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 mit einer @import Deklaration heruntergeladen wird.

Der Grund dafür ist, dass die CSS-Datei, die eine @import-Deklaration enthält, zuerst heruntergeladen werden muss, damit die Deklaration erkannt werden kann. Das führt zu einer sogenannten Anfragekette , die im Fall von CSS die Zeit verzögert, die für das erste Rendering einer Seite benötigt wird. Ein weiterer Nachteil ist, dass Stylesheets, die mit einer @import-Deklaration geladen werden, vom Preload-Scanner nicht erkannt werden können und daher zu Ressourcen werden, die das Rendering blockieren und erst spät erkannt werden.

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

In den meisten Fällen können Sie die @import durch ein <link rel="stylesheet"> Element ersetzen. <link> Elemente ermöglichen das Herunterladen von Stylesheets gleichzeitig, wodurch die gesamte Ladezeit verkürzt wird. Im Gegensatz zu @import Deklarationen, die Stylesheets nacheinander herunterladen.

Kritisches CSS inline einbinden

Die Zeit, die zum Herunterladen von CSS-Dateien benötigt wird, kann den FCP einer Seite erhöhen. Durch das Inline-Einbinden kritischer Stile im <head> des Dokuments wird die Netzwerkanfrage für eine CSS-Ressource vermieden. Wenn es richtig gemacht wird, können sich die anfänglichen Ladezeiten verbessern, wenn der Browsercache eines Nutzers nicht gefüllt ist. Das restliche CSS 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>

Andererseits werden durch das Inline-Einbinden einer großen Menge CSS mehr Byte zur anfänglichen HTML-Antwort hinzugefügt. Da HTML-Ressourcen oft nicht sehr lange oder gar nicht im Cache gespeichert werden können, wird das Inline-CSS nicht für nachfolgende Seiten im Cache gespeichert, die möglicherweise dasselbe CSS in externen Stylesheets verwenden. Testen und messen Sie die Leistung Ihrer Seite, um sicherzustellen, dass die Kompromisse den Aufwand wert sind.

CSS-Demos

JavaScript

JavaScript ist für die meiste Interaktivität im Web verantwortlich, aber das hat seinen Preis. Wenn zu viel JavaScript verwendet wird, kann die Reaktion Ihrer Webseite beim Laden der Seite langsam sein. Es kann sogar zu Problemen mit der Reaktionsfähigkeit kommen, die Interaktionen verlangsamen. Beides kann für Nutzer frustrierend sein.

JavaScript, das das Rendering blockiert

Beim Laden von <script> Elementen ohne die defer oder async Attribute blockiert der Browser das Parsen und Rendern, bis das Skript heruntergeladen, geparst und ausgeführt wurde. Ebenso 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 verzögert. Es gibt jedoch einige wichtige Unterschiede zwischen async und defer.

Eine Darstellung verschiedener Mechanismen zum Laden von Skripts, in denen die Rollen von Parser, Abruf und Ausführung basierend auf verschiedenen Attributen wie „async“, „defer“, „type=&#39;module&#39;“ und einer Kombination aus allen drei beschrieben werden.
Quelle: https://html.spec.whatwg.org/multipage/scripting.html

Mit async geladene Skripts werden sofort nach dem Herunterladen geparst und ausgeführt, während mit defer geladene Skripts ausgeführt werden, wenn das Parsen des HTML-Dokuments abgeschlossen ist. Das geschieht gleichzeitig mit dem DOMContentLoaded-Ereignis des Browsers. Außerdem werden async-Skripts möglicherweise in einer anderen Reihenfolge ausgeführt, während defer-Skripts in der Reihenfolge ausgeführt werden, in der sie im Markup erscheinen.

Clientseitiges Rendering

Im Allgemeinen sollten Sie JavaScript nicht verwenden, um kritische Inhalte oder das LCP-Element einer Seite zu rendern. Das wird als clientseitiges Rendering bezeichnet und ist eine Technik, die häufig in Single-Page-Anwendungen (SPAs) verwendet wird.

Markup, das mit JavaScript gerendert wird, umgeht den Preload-Scanner, da die Ressourcen im clientseitig gerenderten Markup nicht von ihm erkannt werden können. Das kann den Download wichtiger Ressourcen wie eines LCP-Bildes verzögern. Der Browser beginnt erst mit dem Herunterladen des LCP-Bildes, nachdem das Skript ausgeführt und das Element dem DOM hinzugefügt wurde. Das Skript kann wiederum erst ausgeführt werden, nachdem es erkannt, heruntergeladen und geparst wurde. Das wird als kritische Anfrage kette bezeichnet und sollte vermieden werden.

Außerdem ist es wahrscheinlicher, dass beim Rendern von Markup mit JavaScript lange Aufgaben entstehen als bei Markup, das als Antwort auf eine Navigations anfrage vom Server heruntergeladen wird. Die umfangreiche Verwendung von clientseitigem Rendering von HTML kann sich negativ auf die Interaktionslatenz auswirken. Das gilt insbesondere dann, wenn das DOM einer Seite sehr groß ist, was zu erheblicher Renderingarbeit führt, wenn JavaScript das DOM ändert.

Reduzierung

Ähnlich wie bei CSS wird durch das Reduzieren von JavaScript die Dateigröße einer Skriptressource verringert. Das kann zu schnelleren Downloads führen, sodass der Browser schneller mit dem Parsen und Kompilieren von JavaScript fortfahren kann.

Außerdem geht die Reduzierung von JavaScript einen Schritt weiter als die Reduzierung anderer Assets wie CSS. Wenn JavaScript reduziert wird, werden nicht nur Leerzeichen, Tabulatoren und Kommentare entfernt, sondern auch Symbole im Quell-JavaScript werden verkürzt. Dieser Prozess wird manchmal als Uglification bezeichnet. Um den Unterschied zu sehen, nehmen Sie den folgenden JavaScript-Quellcode:

// 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 uglifiziert wird, kann das Ergebnis so 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 für Menschen lesbare Variable scriptElement in der Quelle zu t verkürzt wurde. Bei einer großen Anzahl von Skripts können die Einsparungen erheblich sein, ohne die Funktionen zu beeinträchtigen, die das Produktions-JavaScript einer Website bietet.

Wenn Sie einen Bundler verwenden, um den Quellcode Ihrer Website zu verarbeiten, wird die Uglification oft automatisch für Produktions-Builds durchgeführt. Uglifier wie Terser sind auch hochgradig konfigurierbar, sodass Sie die Aggressivität des Uglification-Algorithmus anpassen können, um maximale Einsparungen zu erzielen. Die Standardeinstellungen für jedes Uglification-Tool reichen jedoch in der Regel aus, um das richtige Gleichgewicht zwischen Ausgabegröße und Beibehaltung der Funktionen zu finden.

JavaScript-Demos

Wissen testen

Wie lassen sich mehrere CSS-Dateien am besten im Browser laden?

Die CSS-Deklaration @import.
Bitte versuchen Sie es noch einmal.
Mehrere <link> Elemente.
Richtig!

Was macht der Preload-Scanner des Browsers?

Er ist ein sekundärer HTML-Parser, der Roh-Markup untersucht, um Ressourcen zu finden, bevor der DOM-Parser sie finden kann, damit sie früher erkannt werden.
Richtig!
Er erkennt <link rel="preload">-Elemente in einer HTML-Ressource.
Bitte versuchen Sie es noch einmal.

Warum blockiert der Browser standardmäßig vorübergehend das Parsen von HTML, wenn JavaScript-Ressourcen heruntergeladen werden?

Um ein FOUC (Flash of Unstyled Content) zu verhindern.
Bitte versuchen Sie es noch einmal.
Da die Auswertung von JavaScript eine sehr CPU-intensive Aufgabe ist und das Anhalten HTML-Parsens der CPU mehr Bandbreite gibt, um das Laden von Skripts abzuschließen.
Bitte versuchen Sie es noch einmal.
Da Skripts das DOM ändern oder anderweitig darauf zugreifen können.
Richtig!

Nächstes Modul: Browser mit Ressourcenhinweisen unterstützen

Nachdem Sie nun wissen, wie sich Ressourcen, die im <head> Element geladen werden, auf das anfängliche Laden der Seite und verschiedene Messwerte auswirken können, ist es Zeit, weiterzumachen. Im nächsten Modul werden Ressourcenhinweise behandelt 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 er es sonst tun würde.