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-imagegeladen 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 dynamischemimport()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.
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.
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?
@import.<link> Elemente.Was macht der Preload-Scanner des Browsers?
<link rel="preload">-Elemente in
einer HTML-Ressource.
Warum blockiert der Browser standardmäßig vorübergehend das Parsen von HTML, wenn JavaScript-Ressourcen heruntergeladen werden?
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.