Webleistung leicht gemacht – Google I/O 2018

Auf der Google I/O 2018 haben wir eine Reihe von Tools, Bibliotheken und Optimierungstechniken vorgestellt, mit denen sich die Webleistung leichter verbessern lässt. Hier erklären wir sie anhand der App „The Oodles Theater“. Außerdem sprechen wir über unsere Experimente mit vorausschauendem Laden und die neue Guess.js-Initiative.

Ewa Gasperowicz

Wir haben im letzten Jahr viel Zeit damit verbracht, herauszufinden, wie wir das Web schneller und leistungsfähiger machen können. Daraus sind neue Tools, Ansätze und Bibliotheken entstanden, die wir in diesem Artikel vorstellen möchten. Im ersten Teil zeigen wir Ihnen einige Optimierungstechniken, die wir bei der Entwicklung der Oodles Theater-App in der Praxis eingesetzt haben. Im zweiten Teil sprechen wir über unsere Experimente mit vorausschauendem Laden und der neuen Guess.js-Initiative.

Leistungsanforderungen

Das Internet wird jedes Jahr schwerer und schwerer. Wenn wir uns den Zustand des Webs ansehen, stellen wir fest, dass eine durchschnittliche Seite auf Mobilgeräten etwa 1,5 MB groß ist, wobei der Großteil davon JavaScript und Bilder sind.

Die zunehmende Größe der Websites trägt zusammen mit anderen Faktoren wie Netzwerklatenz, CPU-Einschränkungen, Render-Blocking-Mustern oder überflüssigem Drittanbietercode zu den komplizierten Leistungsproblemen bei.

Die meisten Nutzer bewerten die Geschwindigkeit als den wichtigsten Aspekt der UX-Hierarchie ihrer Anforderungen. Das ist nicht überraschend, da Sie erst dann viel tun können, wenn eine Seite vollständig geladen ist. Sie können der Seite keinen Wert entnehmen und ihre Ästhetik nicht bewundern.

UX-Hierarchiepyramide
Abbildung 1: Wie wichtig ist Geschwindigkeit für Nutzer? (Speed Matters, Vol. 3)

Wir wissen, dass die Leistung für Nutzer wichtig ist, aber es kann auch schwierig sein, herauszufinden, wo man mit der Optimierung beginnen sollte. Zum Glück gibt es Tools, die Ihnen dabei helfen können.

Lighthouse – eine Grundlage für den Leistungs-Workflow

Lighthouse ist Teil der Chrome-Entwicklertools. Damit können Sie Ihre Website prüfen und erhalten Hinweise, wie Sie sie verbessern können.

Vor Kurzem haben wir eine Reihe neuer Leistungsprüfungen eingeführt, die im täglichen Entwicklungsablauf sehr nützlich sind.

Neue Lighthouse-Prüfungen
Abbildung 2. Neue Lighthouse-Prüfungen

Sehen wir uns anhand eines praktischen Beispiels an, wie Sie sie nutzen können: Die Oodles Theater App. Das ist eine kleine Demo-Web-App, in der Sie einige unserer interaktiven Lieblings-Google Doodles ausprobieren und sogar ein oder zwei Spiele spielen können.

Bei der Entwicklung der App wollten wir sicherstellen, dass sie so leistungsfähig wie möglich ist. Der Ausgangspunkt für die Optimierung war ein Lighthouse-Bericht.

Lighthouse-Bericht für die Oodles-App
Abbildung 3. Lighthouse-Bericht für die Oodles-App

Die anfängliche Leistung unserer App im Lighthouse-Bericht war ziemlich schlecht. In einem 3G-Netzwerk musste der Nutzer 15 Sekunden warten, bis die Inhalte weitgehend gezeichnet waren oder die App interaktiv wurde. Lighthouse hat eine Vielzahl von Problemen mit unserer Website aufgezeigt, was sich auch im Gesamtleistungswert von 23 widerspiegelte.

Die Seite war etwa 3,4 MB groß – wir mussten dringend etwas abspecken.

Das war unsere erste Leistungsherausforderung: Dinge finden, die wir leicht entfernen können, ohne die allgemeine Nutzererfahrung zu beeinträchtigen.

Möglichkeiten zur Leistungsoptimierung

Nicht benötigte Ressourcen entfernen

Es gibt einige offensichtliche Dinge, die sicher entfernt werden können: Leerzeichen und Kommentare.

Vorteile der Minimierung
Abb. 4. JavaScript und CSS reduzieren und komprimieren

Lighthouse hebt diese Möglichkeit in der Prüfung auf nicht minimierten CSS- und JavaScript-Code hervor. Wir haben webpack für unseren Build-Prozess verwendet. Um die Reduzierung zu erreichen, haben wir einfach das Uglify JS-Plug-in verwendet.

Die Reduzierung ist eine gängige Aufgabe. Sie sollten also für jeden Build-Prozess, den Sie verwenden, eine vorgefertigte Lösung finden können.

Eine weitere nützliche Prüfung in diesem Bereich ist Textkomprimierung aktivieren. Es gibt keinen Grund, unkomprimierte Dateien zu senden, und die meisten CDNs unterstützen dies heutzutage standardmäßig.

Wir haben Firebase Hosting verwendet, um unseren Code zu hosten. Firebase aktiviert standardmäßig die GZIP-Komprimierung. Da wir unseren Code auf einem geeigneten CDN gehostet haben, haben wir diese Funktion also kostenlos erhalten.

gzip ist eine sehr beliebte Methode zur Komprimierung, aber auch andere Mechanismen wie Zopfli und Brotli werden immer beliebter. Brotli wird von den meisten Browsern unterstützt. Sie können eine Binärdatei verwenden, um Ihre Assets vorzukomprimieren, bevor Sie sie an den Server senden.

Effiziente Cache-Richtlinien verwenden

Im nächsten Schritt wollten wir sicherstellen, dass wir Ressourcen nicht unnötig zweimal senden.

Die Prüfung Ineffiziente Cache-Richtlinie in Lighthouse hat uns gezeigt, dass wir unsere Caching-Strategien optimieren können, um genau das zu erreichen. Durch das Festlegen eines „max-age“-Ablaufheaders auf unserem Server haben wir dafür gesorgt, dass der Nutzer bei einem wiederholten Besuch die zuvor heruntergeladenen Ressourcen wiederverwenden kann.

Idealerweise sollten Sie so viele Ressourcen wie möglich so sicher wie möglich für einen möglichst langen Zeitraum im Cache speichern und Validierungstokens für eine effiziente Revalidierung der aktualisierten Ressourcen bereitstellen.

Entfernen Sie nicht verwendeten Code.

Bisher haben wir die offensichtlichen Teile des unnötigen Downloads entfernt, aber was ist mit den weniger offensichtlichen Teilen? Beispiel: nicht verwendeter Code.

Codeabdeckung in den Entwicklertools
Abbildung 5: Codeabdeckung prüfen

Manchmal fügen wir unseren Apps Code hinzu, der nicht wirklich notwendig ist. Das passiert vor allem, wenn Sie über einen längeren Zeitraum an Ihrer App arbeiten, sich Ihr Team oder Ihre Abhängigkeiten ändern und manchmal eine verwaiste Bibliothek zurückbleibt. Genau das ist uns passiert.

Am Anfang haben wir die Material Components-Bibliothek verwendet, um schnell einen Prototyp unserer App zu erstellen. Mit der Zeit haben wir uns für ein individuelleres Design entschieden und die Bibliothek ganz vergessen. Glücklicherweise konnten wir sie dank der Codeabdeckungsprüfung in unserem Bundle wiederfinden.

Sie können die Statistiken zur Codeabdeckung in den DevTools sowohl für die Laufzeit als auch für die Ladezeit Ihrer Anwendung prüfen. Im Screenshot unten sind zwei große rote Streifen zu sehen. Über 95 % unseres CSS-Codes und ein großer Teil des JavaScript-Codes wurden nicht verwendet.

Lighthouse hat dieses Problem auch bei der Prüfung auf nicht verwendete CSS-Regeln erkannt. Es wurde eine potenzielle Einsparung von über 400 KB angezeigt. Wir haben uns also den Code noch einmal angesehen und sowohl den JavaScript- als auch den CSS-Teil dieser Bibliothek entfernt.

Wenn wir den MVC-Adapter weglassen, sinkt die Größe der Stile auf 10 KB.
Abbildung 6: Wenn wir den MVC-Adapter weglassen, sinkt die Größe unserer Stile auf 10 KB.

Dadurch wurde unser CSS-Bundle um das 20-Fache verkleinert, was für einen winzigen, zweizeiligen Commit ziemlich gut ist.

Natürlich ist dadurch unser Leistungswert gestiegen und auch die Time to Interactive hat sich deutlich verbessert.

Bei solchen Änderungen reicht es jedoch nicht aus, nur die Messwerte und Bewertungen zu prüfen. Das Entfernen von tatsächlichem Code ist nie risikofrei. Sie sollten daher immer auf potenzielle Regressionen achten.

Unser Code wurde zu 95 % nicht verwendet – irgendwo gibt es also noch diese 5 %. Offenbar hat eine unserer Komponenten noch die Stile aus dieser Bibliothek verwendet – die kleinen Pfeile im Doodle-Schieberegler. Da es sich aber um ein kleines Projekt handelte, konnten wir diese Stile einfach manuell wieder in die Schaltflächen einfügen.

Buttons wurden durch fehlende Bibliothek beschädigt
Abbildung 7: Eine Komponente hat die entfernte Bibliothek noch verwendet.

Wenn Sie also Code entfernen, sollten Sie einen geeigneten Testworkflow einrichten, um sich vor potenziellen visuellen Regressionen zu schützen.

Enorme Netzwerknutzlasten vermeiden

Große Ressourcen können den Seitenaufbau verlangsamen. Sie können unsere Nutzer Geld kosten und sich stark auf ihre Datentarife auswirken. Es ist also wirklich wichtig, darauf zu achten.

Lighthouse konnte mithilfe der Prüfung Enorme Netzwerknutzlast ein Problem mit einigen unserer Netzwerknutzlasten erkennen.

Enorme Netzwerknutzlasten erkennen
Abb. 8: Enorme Netzwerknutzlasten erkennen

Wir haben festgestellt, dass wir über 3 MB Code ausgeliefert haben – das ist ziemlich viel, insbesondere auf Mobilgeräten.

Ganz oben in dieser Liste wurde in Lighthouse hervorgehoben, dass wir ein JavaScript-Anbieter-Bundle mit 2 MB unkomprimiertem Code hatten. Dieses Problem wird auch von webpack hervorgehoben.

Wie es so schön heißt: Die schnellste Anfrage ist die, die nicht gestellt wird.

Idealerweise sollten Sie den Wert jedes einzelnen Assets messen, das Sie Ihren Nutzern bereitstellen, die Leistung dieser Assets messen und entscheiden, ob es sich lohnt, sie mit der ersten Nutzung bereitzustellen. Manchmal können diese Assets nämlich verzögert oder nach Bedarf geladen oder während der Leerlaufzeit verarbeitet werden.

In unserem Fall hatten wir Glück, da wir es mit vielen JavaScript-Bundles zu tun hatten, denn die JavaScript-Community verfügt über eine Vielzahl von Tools zur Prüfung von JavaScript-Bundles.

Prüfung von JavaScript-Bundles
Abbildung 9: JavaScript-Bundle-Prüfung

Wir haben mit dem Webpack Bundle Analyzer begonnen, der uns mitgeteilt hat, dass wir eine Abhängigkeit namens „unicode“ einbeziehen, die 1,6 MB geparstes JavaScript umfasst, also ziemlich viel.

Anschließend haben wir in unserem Editor mit dem Import Cost-Plug-in für Visual Code die Kosten für jedes importierte Modul visualisiert. So konnten wir herausfinden, welche Komponente Code enthielt, der auf dieses Modul verwies.

Anschließend haben wir zu einem anderen Tool gewechselt, BundlePhobia. Mit diesem Tool können Sie den Namen eines beliebigen NPM-Pakets eingeben und die geschätzte Größe nach der Reduzierung und Komprimierung sehen. Wir haben eine gute Alternative für das von uns verwendete Slug-Modul gefunden, die nur 2,2 KB groß war, und haben sie ausgetauscht.

Das hatte große Auswirkungen auf unsere Leistung. Durch diese Änderung und andere Möglichkeiten, die Größe unseres JavaScript-Bundles zu reduzieren, haben wir 2,1 MB Code eingespart.

Wenn man die mit gzip komprimierte und minimierte Größe dieser Bundles berücksichtigt, konnten wir insgesamt eine Verbesserung von 65 % feststellen. Wir haben festgestellt, dass sich dieser Prozess wirklich lohnt.

Versuchen Sie also generell, unnötige Downloads auf Ihren Websites und in Ihren Apps zu vermeiden. Wenn Sie eine Bestandsaufnahme Ihrer Assets machen und deren Auswirkungen auf die Leistung messen, kann das einen großen Unterschied machen. Sie sollten Ihre Assets also regelmäßig überprüfen.

JavaScript-Bootzeit durch Codeaufteilung verkürzen

Große Netzwerknutzlasten können zwar erhebliche Auswirkungen auf unsere App haben, aber es gibt noch etwas, das wirklich große Auswirkungen haben kann: JavaScript.

JavaScript ist Ihr teuerstes Asset. Wenn Sie auf Mobilgeräten große JavaScript-Bundles senden, kann sich die Interaktion der Nutzer mit den Komponenten der Benutzeroberfläche verzögern. Das bedeutet, dass sie auf die Benutzeroberfläche tippen können, ohne dass etwas Sinnvolles passiert. Deshalb ist es wichtig, dass wir verstehen, warum JavaScript so viel kostet.

So verarbeitet ein Browser JavaScript.

JavaScript-Verarbeitung
Abb. 10: JavaScript-Verarbeitung

Zuerst müssen wir das Skript herunterladen. Wir haben eine JavaScript-Engine, die den Code parsen, kompilieren und ausführen muss.

Diese Phasen nehmen auf einem High-End-Gerät wie einem Desktop-Computer oder einem Laptop, vielleicht sogar auf einem High-End-Smartphone, nicht viel Zeit in Anspruch. Auf einem durchschnittlichen Smartphone kann dieser Vorgang jedoch fünf- bis zehnmal länger dauern. Das verzögert die Interaktivität, daher ist es wichtig, diesen Wert zu verringern.

Damit Sie diese Probleme in Ihrer App leichter erkennen können, haben wir in Lighthouse eine neue Prüfung der JavaScript-Bootzeit eingeführt.

JavaScript-Bootzeit
Abbildung 11. Prüfung der JavaScript-Startzeit

Im Fall der Oodle-App wurde uns mitgeteilt, dass wir 1,8 Sekunden für den JavaScript-Bootvorgang aufgewendet haben. Das Problem war, dass wir alle Routen und Komponenten statisch in ein monolithisches JavaScript-Bundle importiert haben.

Eine Möglichkeit, dieses Problem zu umgehen, ist die Codeaufteilung.

Codeaufteilung ist wie Pizza

Code-Splitting ist die Idee, dass Sie Ihren Nutzern nicht eine ganze Pizza JavaScript geben, sondern nur ein Stück nach dem anderen, wenn sie es brauchen.

Codeaufteilung kann auf Routen- oder Komponentenebene angewendet werden. Es funktioniert hervorragend mit React und React Loadable, Vue.js, Angular, Polymer, Preact und mehreren anderen Bibliotheken.

Wir haben Code-Splitting in unsere Anwendung integriert und sind von statischen Importen zu dynamischen Importen übergegangen. So konnten wir Code asynchron und nach Bedarf per Lazy Loading laden.

Codeaufteilung mit dynamischen Importen
Abbildung 13: Codeaufteilung mit dynamischen Importen

Dadurch wurden nicht nur die Bundles kleiner, sondern auch die JavaScript-Startzeit verkürzt. Die Zeit wurde auf 0,78 Sekunden reduziert, was die App um 56 % schneller macht.

Wenn Sie eine JavaScript-lastige Website erstellen, sollten Sie im Allgemeinen nur Code an den Nutzer senden, den er benötigt.

Nutzen Sie Konzepte wie Code-Splitting, erkunden Sie Ideen wie Tree-Shaking und sehen Sie sich das Repository webpack-libs-optimizations an, um Ideen zu erhalten, wie Sie die Größe Ihrer Bibliothek reduzieren können, wenn Sie Webpack verwenden.

Bilder optimieren

Witz über die Leistung beim Laden von Bildern

In der Oodle-App verwenden wir viele Bilder. Leider war Lighthouse viel weniger begeistert davon als wir. Tatsächlich haben wir bei allen drei bildbezogenen Prüfungen versagt.

Wir haben vergessen, unsere Bilder zu optimieren, sie hatten nicht die richtige Größe und wir könnten auch von der Verwendung anderer Bildformate profitieren.

Überprüfung von Bildern
Abbildung 14: Lighthouse-Bildprüfungen

Wir haben mit der Optimierung unserer Bilder begonnen.

Für eine einmalige Optimierung können Sie visuelle Tools wie ImageOptim oder XNConvert verwenden.

Ein automatisierterer Ansatz besteht darin, Ihrem Build-Prozess einen Schritt zur Bildoptimierung hinzuzufügen, z. B. mit Bibliotheken wie imagemin.

So werden auch Bilder, die Sie in Zukunft hinzufügen, automatisch optimiert. Einige CDNs, z. B. Akamai, oder Drittanbieterlösungen wie Cloudinary, Fastly oder Uploadcare bieten umfassende Lösungen zur Bildoptimierung. Sie können Ihre Bilder also auch einfach bei diesen Diensten hosten.

Wenn Sie das aufgrund von Kosten oder Latenzproblemen nicht möchten, bieten Projekte wie Thumbor oder Imageflow selbst gehostete Alternativen.

Vor und nach der Optimierung
Abb. 15: Vor und nach der Optimierung

Unser Hintergrund-PNG wurde in webpack als groß gekennzeichnet, und das zu Recht. Nachdem wir es auf die richtige Größe für den Darstellungsbereich gebracht und mit ImageOptim optimiert hatten, kamen wir auf 100 KB, was akzeptabel ist.

Durch die Wiederholung dieses Vorgangs für mehrere Bilder auf unserer Website konnten wir die Gesamtgröße der Seiten erheblich reduzieren.

Das richtige Format für animierte Inhalte verwenden

GIFs können sehr teuer werden. Überraschenderweise war das GIF-Format ursprünglich gar nicht als Animationsplattform gedacht. Wenn du zu einem geeigneteren Videoformat wechselst, kannst du also die Dateigröße erheblich reduzieren.

In der Oodle-App haben wir ein GIF als Intro-Sequenz auf der Startseite verwendet. Laut Lighthouse könnten wir über 7 MB einsparen, wenn wir zu einem effizienteren Videoformat wechseln würden. Unser Clip war etwa 7, 3 MB groß, was für eine Website viel zu viel ist.Deshalb haben wir ihn in ein Videoelement mit zwei Quelldateien umgewandelt – eine MP4- und eine WebM-Datei für eine breitere Browserunterstützung.

Animierte GIFs durch Videos ersetzen
Abb. 16. Animierte GIFs durch Videos ersetzen

Wir haben das Tool FFmpeg verwendet, um das animierte GIF in eine MP4-Datei zu konvertieren. Das WebM-Format bietet Ihnen noch größere Einsparungen – die ImageOptim API kann diese Konvertierung für Sie übernehmen.

ffmpeg -i animation.gif -b:v 0 -crf 40 -vf scale=600:-1 video.mp4

Durch diese Umstellung konnten wir über 80 % des Gesamtgewichts einsparen. Dadurch wurde die Größe auf etwa 1 MB reduziert.

Dennoch ist 1 MB eine große Ressource, die über die Leitung übertragen werden muss, insbesondere für Nutzer mit eingeschränkter Bandbreite. Glücklicherweise konnten wir die Effective Type API verwenden, um zu erkennen, dass sie eine langsame Bandbreite haben, und ihnen stattdessen ein viel kleineres JPEG-Bild zur Verfügung stellen.

Diese Schnittstelle verwendet die effektive Umlaufzeit und die Werte für die Downloadgeschwindigkeit, um den Netzwerktyp zu schätzen, den der Nutzer verwendet. Es wird einfach ein String zurückgegeben: „slow 2G“, „2G“, „3G“ oder „4G“. Je nach diesem Wert können wir also, wenn der Nutzer eine Verbindung unter 4G hat, das Videoelement durch das Bild ersetzen.

if (navigator.connection.effectiveType) { ... }

Das beeinträchtigt die Nutzererfahrung zwar ein wenig, aber zumindest ist die Website bei einer langsamen Verbindung nutzbar.

Lazy Loading für Bilder außerhalb des Bildschirms

In Karussells, Schiebereglern oder auf sehr langen Seiten werden Bilder oft geladen, obwohl der Nutzer sie nicht sofort auf der Seite sehen kann.

Lighthouse kennzeichnet dieses Verhalten in der Prüfung „Bilder außerhalb des Bildschirms“ und Sie können es auch im Netzwerkbereich der Entwicklertools sehen. Wenn viele Bilder eingehen, aber nur wenige auf der Seite sichtbar sind, sollten Sie stattdessen Lazy Loading in Betracht ziehen.

Lazy Loading wird im Browser noch nicht nativ unterstützt. Daher müssen wir JavaScript verwenden, um diese Funktion hinzuzufügen. Wir haben die Lazysizes-Bibliothek verwendet, um unseren Oodle-Covern Lazy Loading hinzuzufügen.

<!-- Import library -->
import lazysizes from 'lazysizes'  <!-- or -->
<script src="lazysizes.min.js"></script>

<!-- Use it -->

<img data-src="image.jpg" class="lazyload"/>
<img class="lazyload"
    data-sizes="auto"
    data-src="image2.jpg"
    data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w"/>

Lazysizes ist intelligent, weil es nicht nur die Sichtbarkeitsänderungen des Elements verfolgt, sondern auch proaktiv Elemente vorab abruft, die sich in der Nähe der Ansicht befinden, um die Nutzerfreundlichkeit zu optimieren. Außerdem bietet es eine optionale Integration des IntersectionObserver, mit dem Sie sehr effiziente Sichtbarkeitsprüfungen durchführen können.

Nach dieser Änderung werden unsere Bilder bei Bedarf abgerufen. Wenn Sie sich näher mit diesem Thema befassen möchten, finden Sie unter images.guide eine sehr nützliche und umfassende Ressource.

Browsern helfen, kritische Ressourcen frühzeitig bereitzustellen

Nicht jedes Byte, das an den Browser gesendet wird, ist gleich wichtig. Der Browser weiß das. Viele Browser haben Heuristiken, um zu entscheiden, was zuerst abgerufen werden soll. Manchmal wird CSS vor Bildern oder Skripts abgerufen.

Es könnte hilfreich sein, wenn wir als Autoren der Seite dem Browser mitteilen, was uns wirklich wichtig ist. Glücklicherweise haben Browseranbieter in den letzten Jahren eine Reihe von Funktionen hinzugefügt, die uns dabei helfen, z.B. Ressourcenhinweise wie link rel=preconnect, preload oder prefetch.

Diese Funktionen, die auf der Webplattform eingeführt wurden, helfen dem Browser, die richtigen Inhalte zur richtigen Zeit abzurufen. Sie können etwas effizienter sein als einige der benutzerdefinierten, logikbasierten Ladeansätze, die stattdessen mit Skripten umgesetzt werden.

Sehen wir uns an, wie Lighthouse uns dabei hilft, einige dieser Funktionen effektiv zu nutzen.

Als Erstes empfiehlt Lighthouse, mehrere kostspielige Roundtrips zu einem Ursprung zu vermeiden.

Vermeiden Sie mehrere, kostspielige Hin- und Rückflüge zu einem Ursprung.
Abbildung 17: Vermeiden Sie mehrere, kostspielige Hin- und Rückflüge zu einem beliebigen Ursprung.

In der Oodle-App verwenden wir Google Fonts sehr intensiv. Wenn Sie ein Google Fonts-Stylesheet auf Ihrer Seite einfügen, wird es mit bis zu zwei Subdomains verbunden. Lighthouse sagt uns, dass wir durch das Aufwärmen dieser Verbindung bis zu 300 Millisekunden bei der ersten Verbindungszeit einsparen könnten.

Mit „link rel preconnect“ können wir diese Verbindungslatenz effektiv maskieren.

Insbesondere bei Google Fonts, wo das CSS für die Schriftart auf googleapis.com und die Schriftartressourcen auf Gstatic gehostet werden, kann dies erhebliche Auswirkungen haben. Wir haben diese Optimierung also angewendet und ein paar Hundert Millisekunden eingespart.

Als Nächstes schlägt Lighthouse vor, wichtige Anfragen vorab zu laden.

Schlüsselanfragen vorab laden
Abbildung 18: Wichtige Anfragen vorab laden

<link rel=preload> ist sehr leistungsstark. Es informiert den Browser darüber, dass eine Ressource für die aktuelle Navigation benötigt wird, und versucht, den Browser dazu zu bringen, sie so schnell wie möglich abzurufen.

Lighthouse empfiehlt uns hier, die wichtigsten Web-Schriftartressourcen vorab zu laden, da wir zwei Web-Schriftarten laden.

Das Vorabladen einer Webschriftart sieht so aus: Sie geben rel=preload an, übergeben as mit dem Schriftarttyp und geben dann den Schriftarttyp an, den Sie laden möchten, z. B. woff2.

Die Auswirkungen auf Ihre Seite können erheblich sein.

Auswirkungen des Vorabladens von Ressourcen
Abbildung 19: Auswirkungen des Vorabladens von Ressourcen

Wenn Sie „link rel preload“ nicht verwenden und Web-Schriftarten für Ihre Seite wichtig sind, muss der Browser normalerweise zuerst Ihren HTML-Code abrufen, Ihr CSS parsen und erst viel später Ihre Web-Schriftarten abrufen.

Wenn Sie „link rel preload“ verwenden, kann der Browser, sobald er Ihren HTML-Code geparst hat, viel früher mit dem Abrufen dieser Webschriftarten beginnen. In unserer App konnten wir so die Zeit, die für das Rendern von Text mit unseren Webschriftarten benötigt wurde, um eine Sekunde verkürzen.

Wenn Sie Schriftarten mit Google Fonts vorab laden möchten, ist das nicht ganz so einfach. Es gibt einen Haken.

Die Google Fonts-URLs, die wir in unseren Stylesheets für unsere Schriftarten angeben, werden vom Fonts-Team ziemlich regelmäßig aktualisiert. Diese URLs können ablaufen oder regelmäßig aktualisiert werden. Wenn Sie die vollständige Kontrolle über das Laden Ihrer Schriftarten haben möchten, empfehlen wir Ihnen, Ihre Webschriftarten selbst zu hosten. Das ist toll, weil Sie so Zugriff auf Dinge wie „link rel preload“ erhalten.

In unserem Fall war das Tool Google Web Fonts Helper sehr nützlich, um einige dieser Webschriftarten offline zu nehmen und lokal einzurichten. Probieren Sie es also aus.

Unabhängig davon, ob Sie Webschriftarten oder JavaScript als Teil Ihrer kritischen Ressourcen verwenden, sollten Sie versuchen, dem Browser zu helfen, Ihre kritischen Ressourcen so schnell wie möglich bereitzustellen.

Experimentell: Prioritätshinweise

Wir haben heute etwas Besonderes für dich. Zusätzlich zu Funktionen wie Ressourcenhinweisen und Preload haben wir an einer brandneuen experimentellen Browserfunktion gearbeitet, die wir „Priority Hints“ nennen.

Priorität für die anfänglich sichtbaren Inhalte festlegen
Abb. 20: Prioritätshinweise

Mit dieser neuen Funktion können Sie dem Browser mitteilen, wie wichtig eine Ressource ist. Sie stellt ein neues Attribut „importance“ mit den Werten „low“, „high“ oder „auto“ bereit.

So können wir die Priorität weniger wichtiger Ressourcen wie nicht kritischer Stile, Bilder oder Fetch API-Aufrufe senken, um Konflikte zu reduzieren. Wir können auch die Priorität wichtigerer Dinge erhöhen, z. B. unserer Hero-Images.

Im Fall unserer Oodle-App hat dies tatsächlich zu einem praktischen Ort geführt, an dem wir optimieren konnten.

Priorität für die anfänglich sichtbaren Inhalte festlegen
Abb. 21. Priorität für den anfänglich sichtbaren Inhalt festlegen

Bevor wir Lazy Loading für unsere Bilder hinzugefügt haben, hat der Browser Folgendes getan: Wir hatten dieses Bilderkarussell mit all unseren Doodles und der Browser hat alle Bilder ganz am Anfang des Karussells mit einer hohen Priorität abgerufen. Leider waren die Bilder in der Mitte des Karussells für die Nutzenden am wichtigsten. Wir haben also die Wichtigkeit der Hintergrundbilder auf sehr niedrig und die der Vordergrundbilder auf sehr hoch gesetzt. Das hatte bei einer langsamen 3G-Verbindung eine Auswirkung von zwei Sekunden darauf, wie schnell wir diese Bilder abrufen und rendern konnten. Das war also eine positive Erfahrung.

Wir hoffen, diese Funktion in einigen Wochen in Canary einführen zu können.

Strategie zum Laden von Webschriftarten

Typografie ist ein grundlegender Bestandteil eines guten Designs. Wenn Sie Webschriftarten verwenden, sollten Sie das Rendern Ihres Textes idealerweise nicht blockieren und auf keinen Fall unsichtbaren Text anzeigen.

Wir weisen darauf jetzt in Lighthouse mit der Prüfung Unsichtbaren Text beim Laden von Web-Schriftarten vermeiden hin.

Unsichtbaren Text beim Laden von Webfonts vermeiden
Abb. 22. Unsichtbaren Text beim Laden von Webfonts vermeiden

Wenn Sie Ihre Webschriftarten mit einem Font-Face-Block laden, überlassen Sie es dem Browser, zu entscheiden, was zu tun ist, wenn das Abrufen der Webschriftart lange dauert. Einige Browser warten bis zu drei Sekunden, bevor sie auf eine Systemschriftart zurückgreifen. Sobald die Schriftart heruntergeladen ist, wird sie schließlich ausgetauscht.

Wir versuchen, diesen unsichtbaren Text zu vermeiden. In diesem Fall hätten wir die klassischen Doodles dieser Woche nicht sehen können, wenn die Webschriftart zu lange gebraucht hätte. Mit der neuen Funktion font-display haben Sie jedoch viel mehr Kontrolle über diesen Prozess.

    @font-face {
      font-family: 'Montserrat';
      font-style: normal;
      font-display: swap;
      font-weight: 400;
      src: local('Montserrat Regular'), local('Montserrat-Regular'),
          /* Chrome 26+, Opera 23+, Firefox 39+ */
          url('montserrat-v12-latin-regular.woff2') format('woff2'),
            /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
          url('montserrat-v12-latin-regular.woff') format('woff');
    }

Mit der Schriftanzeige können Sie festlegen, wie Webschriftarten gerendert oder ersetzt werden, je nachdem, wie lange der Austausch dauert.

In diesem Fall verwenden wir „font-display: swap“. „swap“ gibt der Schriftart eine Blockierungsdauer von null Sekunden und eine unendliche Austauschdauer. Das bedeutet, dass der Browser den Text ziemlich sofort mit einer Fallback-Schriftart zeichnet, wenn das Laden der Schriftart eine Weile dauert. Und sie wird ausgetauscht, sobald die Schriftart verfügbar ist.

In unserer App konnten wir so schon sehr früh aussagekräftigen Text anzeigen und dann zur Webschriftart wechseln, sobald sie bereit war.

Ergebnis der Schriftart-Anzeige
Abbildung 23: Ergebnis der Schriftart-Anzeige

Wenn Sie Webschriftarten verwenden, wie es auf einem Großteil der Websites der Fall ist, sollten Sie eine gute Strategie zum Laden von Webschriftarten haben.

Es gibt viele Webplattform-Funktionen, mit denen ihr das Laden von Schriftarten optimieren könnt. Seht euch aber auch das Web Font Recipes-Repository von Zach Leatherman an, denn es ist wirklich großartig.

Skripts reduzieren, die das Rendering blockieren

Es gibt andere Teile unserer Anwendung, die wir früher in der Downloadkette bereitstellen könnten, um zumindest eine grundlegende Nutzererfahrung etwas früher zu ermöglichen.

Auf dem Lighthouse-Zeitachsenstreifen sehen Sie, dass der Nutzer in den ersten Sekunden, in denen alle Ressourcen geladen werden, keine Inhalte sehen kann.

Möglichkeit zur Reduzierung von Stylesheets, die das Rendering blockieren
Abbildung 24: Möglichkeit zur Reduzierung von Stylesheets, die das Rendering blockieren

Das Herunterladen und Verarbeiten externer Stylesheets blockiert den Rendering-Prozess.

Wir können versuchen, den kritischen Rendering-Pfad zu optimieren, indem wir einige der Stile etwas früher bereitstellen.

Wenn wir die Stile extrahieren, die für dieses erste Rendering verantwortlich sind, und sie in unserem HTML-Code inline einfügen, kann der Browser sie sofort rendern, ohne auf die externen Stylesheets warten zu müssen.

In unserem Fall haben wir ein NPM-Modul namens Critical verwendet, um unsere kritischen Inhalte während eines Build-Schritts in index.html einzubinden.

Dieses Modul hat uns zwar die meiste Arbeit abgenommen, aber es war trotzdem etwas knifflig, es auf verschiedenen Routen reibungslos zum Laufen zu bringen.

Wenn Sie nicht vorsichtig sind oder Ihre Websitestruktur sehr komplex ist, kann es schwierig sein, diese Art von Muster einzuführen, wenn Sie die Anwendungsshell-Architektur nicht von Anfang an eingeplant haben.

Deshalb ist es so wichtig, die Leistung frühzeitig zu berücksichtigen. Wenn Sie nicht von Anfang an auf Leistung achten, ist die Wahrscheinlichkeit hoch, dass Sie später Probleme bekommen.

Am Ende hat sich unser Risiko ausgezahlt, wir haben es geschafft und die App hat Inhalte viel früher bereitgestellt, was die Zeit bis zum ersten sinnvollen Zeichnen erheblich verbessert hat.

Das Ergebnis

Das war eine lange Liste von Leistungsoptimierungen, die wir auf unserer Website vorgenommen haben. Sehen wir uns das Ergebnis an. So wurde unsere App vor und nach der Optimierung auf einem mittelgroßen Mobilgerät in einem 3G-Netzwerk geladen.

Der Lighthouse-Leistungswert stieg von 23 auf 91. Das ist ein ziemlich guter Fortschritt in Bezug auf die Geschwindigkeit. Alle Änderungen wurden durch die kontinuierliche Überprüfung und Befolgung des Lighthouse-Berichts vorangetrieben. Wenn Sie sich ansehen möchten, wie wir alle Verbesserungen technisch implementiert haben, können Sie sich unser Repository ansehen, insbesondere die dortigen PRs.

Vorhersagbare Leistung – datengestützte Nutzererfahrung

Wir sind der Ansicht, dass maschinelles Lernen in vielen Bereichen eine spannende Chance für die Zukunft darstellt. Eine Idee, die hoffentlich in Zukunft zu mehr Experimenten anregt, ist, dass echte Daten die von uns erstellten Nutzererlebnisse wirklich beeinflussen können.

Heute treffen wir viele willkürliche Entscheidungen darüber, was der Nutzer möglicherweise möchte oder benötigt und was daher vorab abgerufen, vorab geladen oder im Pre-Caching gespeichert werden sollte. Wenn wir richtig raten, können wir eine kleine Menge an Ressourcen priorisieren, aber es ist wirklich schwierig, das auf die gesamte Website zu skalieren.

Wir haben heute tatsächlich Daten zur Verfügung, die uns bei der Optimierung helfen. Mit der Google Analytics Reporting API können wir uns die nächste Top-Seite und die Ausstiegsraten für jede URL auf unserer Website ansehen und daraus schließen, welche Ressourcen wir priorisieren sollten.

Wenn wir dies mit einem guten Wahrscheinlichkeitsmodell kombinieren, vermeiden wir, dass die Daten unserer Nutzer durch aggressives Prefetching von Inhalten verschwendet werden. Wir können diese Google Analytics-Daten nutzen und maschinelles Lernen sowie Modelle wie Markov-Ketten oder neuronale Netzwerke verwenden, um solche Modelle zu implementieren.

Datengestützte Bündelung für Webanwendungen
Abb. 25: Datengestützte Bündelung für Web-Apps

Um diese Tests zu vereinfachen, freuen wir uns, eine neue Initiative namens Guess.js ankündigen zu können.

Guess.js
Abb. 26: Guess.js

Guess.js ist ein Projekt, das sich auf datengestützte Nutzererfahrungen im Web konzentriert. Wir hoffen, dass es Sie dazu inspiriert, Daten zu nutzen, um die Webleistung zu verbessern und darüber hinauszugehen. Alles ist Open Source und ab sofort auf GitHub verfügbar. Es wurde in Zusammenarbeit mit der Open-Source-Community von Minko Gechev, Kyle Matthews von Gatsby, Katie Hempenius und einigen anderen entwickelt.

Sehen Sie sich Guess.js an und teilen Sie uns Ihre Meinung mit.

Zusammenfassung

Werte und Messwerte sind hilfreich, um die Geschwindigkeit des Webs zu verbessern, aber sie sind nur die Mittel, nicht die Ziele selbst.

Wir alle haben schon einmal erlebt, dass der Seitenaufbau unterwegs nur langsam geladen wird. Jetzt haben wir die Möglichkeit, unseren Nutzern ein besseres Erlebnis zu bieten, bei dem der Seitenaufbau wirklich schnell geladen wird.

Die Leistungsverbesserung ist ein Prozess. Viele kleine Änderungen können zu großen Verbesserungen führen. Wenn Sie die richtigen Optimierungstools verwenden und die Lighthouse-Berichte im Blick behalten, können Sie Ihren Nutzern eine bessere und inklusivere Erfahrung bieten.

Besonderer Dank geht an: Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino, Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse und Google Doodles.