Pagespeedoptimierung mittels Ressourcenpriorisierung

Pagespeedoptimierung mittels Ressourcenpriorisierung

In diesem Artikel möchte ich Euch diverse Techniken zur Ladezeitoptimierung anhand simpler Anweisungen im <head> Bereich vorstellen. Mittels dieser Techniken ist es möglich, den Browser zur Priorisierung und/oder vorgelagerten Zwischenspeicherung von akut oder bald benötigten Ressourcen und Verbindungen anzuweisen.

Prefetching

Am bekanntesten dürfte die Prefetch-/Link-Prefetch-Anweisung sein.
Was verbirgt sich hinter dieser?

Mittels eines Prefetch-Befehls kann man den Browser anweisen, komplette Ressourcen vorab zu laden und im Cache des Browsers zu speichern. Dies ist sinnvoll um Ressourcen, deren Zugriff man in der Zukunft erwartet – man kann hier beispielsweise auf der Kassenseite eines Onlineshops bereits die Ressourcen für den Checkout mittels Prefetching in den Zwischenspeicher laden. Alternativ funktioniert dies auch für einzelne Ressourcen – bei mehrteiligen Artikeln könnte dies das Titelbild von der erwarteten Folgeseite sein.

Die Ressourcen werden hier nicht beim initialen Laden einer Seite abgerufen, damit diese nicht beeinträchtigt wird, sondern das Prefetching wird in der Regel ausgeführt, wenn der Browser im „idle“-State ist. Das bedeutet, sobald alle regulären Ressourcen der aktuellen Seite geladen wurden, werden die Prefetch-Befehle abgearbeitet.

Die Syntax beim Prefetch lautet:

HTML: <link rel=”prefetch” href=”/uploads/images/pic.png“>
HTTP Header: Link: </uploads/images/pic.png>; rel=prefetch

Beispiele für verschiedene Ressourcen/erwartete Folgeseiten wären:

<link rel=”prefetch” href=”https://www.omt.de” /> <!–Startseite zur Homepage einer Landing Page–>
<link rel=”prefetch” href=”/js/ressource.js/” /><!–Die .js Datei einer erwarteten Folgeseite bereits im Voraus laden–>
<link rel=”prefetch” href=”/img/hero.jpg/” /><!–Das Titelbild einer erwarteten Folgeseite bereits im Voraus laden–>
<link rel=”prerender” href=”/artikel/page2″ /> <!–Folgeseite eines mehrseitigen Artikels–>

Prefetching kann die User-Erfahrung auf einer Seite durch reduzierte Ladezeiten von Folgeseiten stark verbessern, doch die Kunst hierbei ist es, den Weg eines Users vorauszuahnen – was selbstverständlich nicht immer der Fall ist und somit auch die Möglichkeit bietet, Ressourcen zu “verschwenden”. Insbesondere Nutzer von Mobilgeräten dürften dankbar sein, die Ressourcen von zehn weiteren Seiten nicht im Hintergrund zu laden, während sie einen Artikel lesen. Für reduzierte Ladezeit bei richtig gesetzten Befehlen lohnt sich Prefetching jedoch durchaus!

Wie ist die Browser-Unterstützung von Prefetching?

Laut caniuse.com wird Prefetching derzeit von allen gängigen Browsern außer Safari und Opera mini unterstützt.

Prerendering

Eine dem Prefetching relativ ähnliche, ressourcenintensivere Methode ist das Prerendering. Bei diesem Befehl lädt der Browser nicht nur die Ressourcen der anvisierten Seite in den Cache, sondern rendert sie in einem versteckten Tab komplett. Eine somit vorbereitete Seite kann theoretisch komplett unverzögert nach Klick angezeigt werden.

Die Syntax beim Prerender lautet:

<link rel=”prerender” href=”https://www.omg.de”>

Da diese Technik sehr ressourcenintensiv für den Nutzer ist, ist auch hier sehr wichtig, gezielt nur Seiten anzusteuern, deren Besuch als hochwahrscheinlich angesehen werden kann.

Wie ist die Browser-Unterstützung von Prerender?

Laut caniuse.com wird Prerendering derzeit lediglich durch Google Chrome unterstützt.

Für Entwickler: Prefetch und Prerender dynamisch optimieren

Wir testen derzeit ein dynamisches Skript, mit welchem der Browser dynamisch auf Mouseover des Users reagierend <link>-Kommandos mit Prefetch und Prerender generiert. Die Messung der Ergebnisse steht zum Stand dieses Artikels noch aus, doch insbesondere bei der Nutzung in Chrome kam es zu gefühlten, teils deutlichen Verbesserungen auf der ReachX-Seite.

Wer es in seiner *.js Ressource testen möchte, kann gerne den folgenden Code verwenden. Über ein Dankeschön sowie Erfahrungsberichte mit dem Snippet sind wir immer dankbar:

function prefetch_mouseover() {
var links = document.querySelectorAll(‘a’);
[].forEach.call(links, function(link) {
link.addEventListener(“mouseenter”, function () {
$(“link[rel*=’prerender’]”).remove();
var newPreLoadLink = document.createElement(‘link’);
newPreLoadLink.rel = “prerender prefetch”;
newPreLoadLink.href = link.href;
document.head.appendChild(newPreLoadLink);
});
});
}

Zur Erläuterung: In dieser Form werden ALLE Links angewählt. WordPress-Nutzer sollten dieses Skript nicht unverändert in einer globalen scripts.js Datei laden. Ansonsten würde bereits das Mouseover bei Dashboard-Befehlen wie “neuer Eintrag” voraussichtlich zum Erscheinen von “Geister-Posts” führen. Man kann hier gezielt Eltern-Klassen vergeben wie “#content a” oder was für die jeweilige Seite individuell passend erscheint. Weiterhin wird immer nur ein Element erhalten. Das heißt, dass beim zweiten Mouseover der <link>-Befehl des ersten gelöscht wird.

DNS- Prefetching

DNS-Prefetching bedeutet, dass der Browser die Verbindung mit der IP-Adresse, sprich dem Host/der Domain des mit Prefetching „vorgeladenen“ Linkziels, eine Verbindung öffnet bevor der User den Link klickt und die DNS-Resolution durchführt. Dieser Arbeitsschritt wird beim tatsächlichen Klick eingespart und reduziert somit die Ladezeit des Linkziels.
DNS-Resolution bedeutet: Übersetzung des entsprechenden Domainnamens zur technisch lesbaren IP-Adresse (DNS-Resolution = Domain-Name-System-Aufschlüsselung). Für detailliertere Informationen: https://nlp.stanford.edu/IR-book/html/htmledition/dns-resolution-1.html

Der DNS-Prefetching-Prozess kann in die folgenden Abläufe eingeteilt werden:

  1. Beim Besuch einer Website wird diese beim Nutzer geladen und gerendert (optisch dargestellt).
  2. Parallel hierzu werden in der Website eingebundene Links durch den Browser ausgelesen und die Verbindung bzw. Domain-Resolution für deren Ziele durchgeführt.
  3. Wenn ein User einen Link klickt, wurde dessen Ziel-IP bereits gespeichert und der Browser kann ohne Zeitverzögerung mit dem Laden der Inhalte (HTML, CSS, JS, etc.) beginnen.

Empfohlene Einsatzgebiete für das DNS-Prefetching sind beispielsweise Google Fonts, Google Analytics oder ein unter Umständen verwendeter CDN.

Die Syntax beim DNS-Prefetch lautet:

<link rel=”dns-prefetch” href=”//fonts.googleapis.com”>
<link rel=”dns-prefetch” href=”//cdn.domain.com”>
<link rel=”dns-prefetch” href=”//opensource.keycdn.com”>
<link rel=”dns-prefetch” href=”//www.google-analytics.com”>

Wie ist die Browser-Unterstützung von DNS-Prefetch?

Laut caniuse.com wird DNS-Prefetch derzeit von allen gängigen Browsern unterstützt.

Preconnect

Preconnect ist eine dem DNS-Prefetching ähnliche Maßnahme, welche man in Bezug auf Performance verlustfrei parallel anwenden kann, da die Funktionen einander „ergänzen“, d.h. wenn der DNS-Prefetch zuerst ausgeführt wurde, werden dessen Ergebnisse daraufhin im Preconnect weiterverwendet. Es sind mir derzeit keine Benchmarks bekannt, welche messen, ob es Vorteile/Nachteile birgt, beides gleichzeitig zu verwenden, aktuell ist jedoch eher nicht davon auszugehen, dass es nachteilig wäre.

Beispielsweise hat KeyCDN (ein wie dem Namen zu entnehmen ist CDN-Provider, der sehr großen Wert auf Performance legt) beides parallel im Einsatz.

Auszug aus dem Quellcode der Homepage www.keycdn.com:

<!– Prefetch: https://www.keycdn.com/support/prefetching/ –>
<link rel=”dns-prefetch” href=”//cdn.keycdn.com/”>
<link rel=”dns-prefetch” href=”//fonts.googleapis.com/”>
<!– Preconnect: https://www.keycdn.com/support/preconnect/ –>
<link rel=”preconnect” href=”https://cdn.keycdn.com” crossorigin>
<link rel=”preconnect” href=”https://fonts.gstatic.com” crossorigin>

Durch Preconnect öffnet der Browser die Verbindungen zu den entsprechenden Linkzielen. Dies beinhaltet:

  1. DNS Lookup (vgl. https://www.techopedia.com/definition/29029/dns-lookup)
  2. TCP Handshake (vgl. https://www.inetdaemon.com/tutorials/internet/tcp/3-way_handshake.shtml)
  3. TLS Negotiation (vgl. https://hpbn.co/transport-layer-security-tls/)

Hierdurch wird die Ladezeit des Verbindungsaufbaus zum Ziel aus der Betrachtung genommen. Der Google-Programmierer Ilya Grigorik hat in einem Test Case allein durch das Hinzufügen von Preconnect beim Laden der Google Schriftarten ca. 0,5 Sekunden Ladezeit sparen können und einen sehr informativen Artikel zu diesem Thema geschrieben.

Die Syntax beim Preconnect lautet:

<link rel=”preconnect” href=”https://www.google.com”>

Zum Abschluss des Themas ein treffendes Fazit von Ilya Grigorik (welches man auf alle erwähnten Techniken anwenden kann):

Preconnect often, Preconnect wisely
Preconnect is an important tool in your optimization toolbox. As above examples illustrate, it can eliminate many costly roundtrips from your request path – in some cases reducing the request latency by hundreds and even thousands of milliseconds. That said, use it wisely: each open socket incurs costs both on the client and server, and you want to avoid opening sockets that might go unused. As always, apply, measure real-world impact, and iterate to get the best performance mileage from this feature.

Man könnte beispielsweise die folgenden Domains mittels Preconnect im <head> vorab abrufen:

  • https://fonts.googleapis.com
  • https: //www.googletagmanager.com
  • https: //maps.googleapis.com
  • https: //www.google-analytics.com

Es bleibt festzuhalten, dass der Unterschied zum DNS-Prefetching darin liegt, dass DNS-Prefetchins lediglich den oben beschriebenen DNS-Lookup durchführt, während Preconnect zwei weitere Verbindungsmaßnahmen zum Ziel ausführt und somit die benötigten Roundtrips reduziert.

Wie ist die Browser-Unterstützung von Preconnect?

Laut caniuse.com wird Preconnect derzeit von den meisten gängigen Browsern unterstützt, jedoch noch nicht so gut wie DNS-Prefetch.

Preloading

Preloading ist die Anweisung an den Browser, das Linkziel bzw. die entsprechende Datei direkt zu laden, um beispielsweise “unsichtbare” Texte oder Titelbilder, welche sonst erst im Laufe des Ladens eingeblendet werden, zu vermeiden. Die browserseitige Priorität ist hier also höher als beim Prefetching, das Dateien erst als Reserve lädt, sobald der Browser idle ist. Preloading wird demnach für Elemente, die sofort benötigt werden, eingesetzt, um essentielle Ressourcen mit einer hohen Priorität zu versehen und somit Darstellungsfehler minimiert.

Die Syntax beim Preloading lautet:

Preloading eines Bildes:
<link rel=”preload” href=”image.png”>

Preloading von Fonts:
<link rel=“preload” href=“https://example.com/fonts/font.woff” as=“font” crossorigin>
Hinweis: Bei externen Quellen muss das „crossorigin“-Tag ergänzt werden. Für lokal aufgerufene Fonts entfällt dieser Zusatz.

Wie ist die Browser-Unterstützung von Preload?

Die Unterstützung für Preloading ist noch durchwachsen, auf Chrome, Safari und Edge (nicht als http-Befehl) jedoch vorhanden.

http/2 Server Push

Mittels Server Push ist es möglich, verschiedene Dateien mit dem ersten Request an den User zu schicken.
Somit muss der User nicht mehr nach Erhalten der initialen HTML-Datei die darin verlinkten Dateien anfragen, sondern bekommt sie direkt mit der HTML-Datei ausgeliefert.
Hiermit können die sogenannten Roundtrips (Anfrage an den Server und Erhalt der Daten) und die daraus resultierenden Ladezeiten drastisch reduziert werden.

Der Prozess ohne und mit http/2 Server Push lässt sich Folgendermaßen darstellen:


Regulär wird dieses mittels einer Variation des Preload-Tags durchgeführt:
Link: </css/styles.css>; rel=preload; as=style

Entscheidend ist hierbei der Anhang „as=style“.

Alternativ kann man den Server Push nun via PHP auf die folgende Art durchführen:
header(“Link: </css/styles.css>; rel=preload; as=style”);

Es können auch mehrere Dateien auf diese Art ausgegeben werden:
Link: </css/styles.css>; rel=preload; as=style, </js/scripts.js>; rel=preload; as=script, </img/logo.png>; rel=preload; as=image

Und es ist letztendlich möglich, Push-Requests mit beispielsweise Preconnect in einem Befehl zu verknüpfen:
Link: </css/styles.css>; rel=preload; as=style, <https://fonts.gstatic.com>; rel=preconnect

Alternative Steuerung mittels der .htaccess: Hier wird der Server angewiesen, bei jedem Aufruf einer HTML-Datei, das anvisierte .css-Stylesheet automatisch im gleichen Roundtrip mitzuliefern:

<FilesMatch “\.html$”>
Header set Link “</css/styles.css>; rel=preload; as=style”
<FilesMatch>

Fazit:

Wir können mittels der oben beschriebenen Techniken die direkten und indirekten Ladezeiten von unseren Seiten oder Linkzielen positiv beeinflussen.
Eine direkte Verbesserung ist durch Preconnect sowohl auf externe Ressourcen (Google Fonts, Analytics, Tag Manager, evtl. Facebook und andere Tracking Apps) zu erwarten als auch durch das gezielte oder dynamische Pre-Abrufen von Seiten via Prefetching/Prerendering/Preloading. Im Optimalfall wird Letzteres via http2/ Server Pushing von internen Ressourcen durchgeführt.