Web Performance Made Easy – Google I/O 2018

Auf der Google I/O 2018 haben wir eine Zusammenfassung von Tools, Bibliotheken und Optimierungstechniken vorgestellt, die die Verbesserung der Webleistung erleichtern. Wir erklären sie anhand der App „The Oodles Theater“ und berichten auch über unsere Experimente mit Predictive Loading und die neue Guess.js-Initiative.

Ewa Gasperowicz

Wir haben das letzte Jahr 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 Ihnen in diesem Artikel vorstellen möchten. Im ersten Teil stellen wir einige Optimierungstechniken vor, die wir bei der Entwicklung der Oodles Theater App verwendet haben. Im zweiten Teil geht es um unsere Experimente mit Predictive Loading und die neue Guess.js-Initiative.

Leistungsanforderungen

Das Internet wird jedes Jahr 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.Der Großteil davon besteht aus JavaScript und Bildern.

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

Die meisten Nutzer bewerten die Geschwindigkeit als sehr wichtig in der UX-Hierarchie ihrer Anforderungen. Das ist nicht verwunderlich, da Sie nicht viel tun können, bevor eine Seite vollständig geladen ist. Sie können keinen Wert aus der Seite ableiten 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. Es kann sich aber auch wie ein Geheimnis anfühlen, herauszufinden, wo man mit der Optimierung beginnen soll. Glücklicherweise gibt es Tools, die Ihnen dabei helfen können.

Lighthouse – Grundlage für den Leistungs-Workflow

Lighthouse ist Teil der Chrome-Entwicklertools und ermöglicht es Ihnen, Ihre Website zu prüfen und Hinweise zur Verbesserung zu erhalten.

Wir haben vor Kurzem eine Reihe von neuen 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 an einem praktischen Beispiel an, wie Sie sie nutzen können: Die Oodles Theater App ist eine kleine Demo-Web-App, in der Sie einige unserer beliebtesten interaktiven Google-Doodles ausprobieren und sogar ein oder zwei Spiele spielen können.

Bei der Entwicklung der App war es uns wichtig, dass sie so leistungsstark wie möglich ist. 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 war laut Lighthouse-Bericht ziemlich schlecht. In einem 3G-Netzwerk musste der Nutzer 15 Sekunden warten, bis die erste sinnvolle Darstellung erfolgte oder die App interaktiv wurde. Lighthouse hat eine Vielzahl von Problemen auf unserer Website gemeldet und die Gesamtleistung von 23 hat das genau widergespiegelt.

Die Seite wog etwa 3,4 MB – wir mussten unbedingt etwas abspecken.

Das war der Beginn unserer ersten Leistungsherausforderung: Wir mussten Dinge finden, die wir einfach entfernen konnten, ohne die Nutzerfreundlichkeit zu beeinträchtigen.

Möglichkeiten zur Leistungsoptimierung

Unnötige Ressourcen entfernen

Einige offensichtliche Dinge können problemlos entfernt werden: Leerzeichen und Kommentare.

Vorteile der Komprimierung
Abbildung 4. JavaScript und CSS reduzieren und komprimieren

Lighthouse weist in der Prüfung „Nicht minimiertes CSS und JavaScript“ auf diese Optimierungsmöglichkeit hin. Wir haben webpack für unseren Build-Prozess verwendet. Um die Minimierung zu erreichen, haben wir einfach das Uglify JS-Plug-in verwendet.

Die Minimierung ist eine häufige Aufgabe. Daher sollten Sie für jeden Build-Prozess, den Sie verwenden, eine fertige Lösung finden.

Ein weiterer nützlicher Audit 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 zum Hosten unseres Codes verwendet. Firebase ermöglicht standardmäßig Gzipping. Durch das Hosten unseres Codes in einem angemessenen CDN haben wir das also kostenlos erhalten.

Gzip ist zwar 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 ein Binärprogramm verwenden, um Ihre Assets vor dem Senden an den Server vorzukomprimieren.

Effiziente Cache-Richtlinien verwenden

Im nächsten Schritt haben wir dafür gesorgt, dass Ressourcen nicht unnötig zweimal gesendet werden.

Der Lighthouse-Audit Inefficient cache policy (Ineffiziente Cache-Richtlinie) hat uns darauf aufmerksam gemacht, dass wir unsere Caching-Strategien optimieren können, um genau das zu erreichen. Durch das Festlegen eines Ablauf-Headers vom Typ „max-age“ auf unserem Server haben wir dafür gesorgt, dass Nutzer bei einem erneuten Besuch die zuvor heruntergeladenen Ressourcen wiederverwenden können.

Im Idealfall sollten Sie so viele Ressourcen wie möglich für den längstmöglichen Zeitraum im Cache speichern und Validierungstokens für die effiziente erneute Validierung der aktualisierten Ressourcen bereitstellen.

Nicht verwendeten Code entfernen

Bisher haben wir die offensichtlichen Teile des unnötigen Downloads entfernt. Was ist aber mit den weniger offensichtlichen Teilen? Das kann beispielsweise nicht verwendeter Code sein.

Codeabdeckung in den Entwicklertools
Abbildung 5. Codeabdeckung prüfen

Manchmal ist in unseren Apps Code enthalten, 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. Im Laufe der Zeit haben wir uns für ein benutzerdefiniertes Erscheinungsbild entschieden und die Bibliothek ganz vergessen. Glücklicherweise konnten wir es mithilfe der Code-Coverage-Prü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 aufrufen. Im unteren Screenshot sehen Sie die beiden großen roten Streifen. Über 95 % unseres CSS und ein großer Teil des JavaScripts wurden nicht verwendet.

Lighthouse hat dieses Problem auch im Audit „Nicht verwendete CSS-Regeln“ erkannt. Es wurde ein potenzielles Einsparpotenzial von über 400 KB angezeigt. Wir haben uns also wieder unserem Code zugewandt und sowohl den JavaScript- als auch den CSS-Teil dieser Bibliothek entfernt.

Wenn wir den MVC-Adapter entfernen, sinkt die Größe unserer Styles auf 10 KB.
Abbildung 6. Wenn wir den MVC-Adapter entfernen, sinkt die Größe unserer Styles auf 10 KB.

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

Das hat natürlich unseren Leistungsindex erhöht und auch die Zeit bis zur Interaktivität deutlich verbessert.

Bei solchen Änderungen reicht es jedoch nicht aus, nur Ihre Messwerte und Werte 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. Die restlichen 5% sind aber noch irgendwo vorhanden. Offenbar wurde in einer unserer Komponenten noch auf die Stile aus dieser Bibliothek verwiesen – die kleinen Pfeile im Doodle-Slider. Da es sich aber um so wenige handelte, konnten wir diese Stile einfach manuell wieder in die Schaltflächen einfügen.

Schaltflächen funktionieren nicht mehr, weil eine Bibliothek fehlt
Abbildung 7. Eine Komponente hat die entfernte Bibliothek noch verwendet.

Wenn Sie Code entfernen, sollten Sie also einen geeigneten Testworkflow einrichten, um potenzielle visuelle Regressionen zu vermeiden.

Sehr große Netzwerknutzlasten vermeiden

Wir wissen, dass große Ressourcen das Laden von Webseiten verlangsamen können. Sie können unsere Nutzer Geld kosten und sich erheblich auf ihre Datentarife auswirken. Daher ist es sehr wichtig, dies zu berücksichtigen.

Lighthouse hat mithilfe des Audits Enormous network payload (Enorme Netzwerk-Payload) ein Problem mit einigen unserer Netzwerk-Payloads erkannt.

Sehr große Netzwerknutzlasten erkennen
Abbildung 8. Sehr große Netzwerknutzlasten erkennen

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

Ganz oben in dieser Liste hat Lighthouse darauf hingewiesen, dass wir ein JavaScript-Anbieter-Bundle mit 2 MB unkomprimiertem Code hatten. Das ist auch ein Problem, das von webpack hervorgehoben wird.

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

Im Idealfall sollten Sie den Wert jedes einzelnen Assets messen, das Sie an Ihre Nutzer senden, die Leistung dieser Assets messen und entscheiden, ob es sich lohnt, sie mit der ursprünglichen Nutzererfahrung zu senden. Manchmal können diese Assets aufgeschoben oder verzögert geladen oder in der Leerlaufzeit verarbeitet werden.

Da wir es mit vielen JavaScript-Bundles zu tun haben, hatten wir Glück, weil die JavaScript-Community eine Vielzahl von Tools zur Überprüfung von JavaScript-Bundles entwickelt hat.

JavaScript-Bundle-Auditing
Abbildung 9. JavaScript-Bundle-Prüfung

Wir haben mit dem Webpack-Bundle-Analyzer begonnen, der uns mitteilte, dass wir eine Abhängigkeit namens „unicode“ einbinden, die 1,6 MB geparstes JavaScript umfasste, also ziemlich viel.

Dann haben wir unseren Editor aufgerufen und mit dem Import Cost Plugin für Visual Code konnten wir die Kosten jedes Moduls visualisieren, das wir importiert haben. So konnten wir herausfinden, in welcher Komponente Code enthalten war, der auf dieses Modul verwies.

Dann sind wir zu einem anderen Tool gewechselt: BundlePhobia. Mit diesem Tool können Sie den Namen eines beliebigen NPM-Pakets eingeben und sehen, wie groß es nach der Minimierung und Komprimierung mit Gzip voraussichtlich sein wird. Wir haben eine gute Alternative für das Slug-Modul gefunden, das wir verwendet haben. Es wiegt nur 2,2 KB. Deshalb haben wir es ersetzt.

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

Insgesamt konnten wir eine Verbesserung von 65% feststellen, wenn man die gezippte und minimierte Größe dieser Bundles berücksichtigt. Und wir haben festgestellt, dass sich das als Prozess wirklich lohnt.

Vermeiden Sie daher unnötige Downloads auf Ihren Websites und in Ihren Apps. Wenn Sie Ihre Assets inventarisieren und ihre Leistung messen, kann das einen großen Unterschied machen. Prüfen Sie Ihre Assets daher regelmäßig.

JavaScript-Boot-up-Zeit mit Code-Splitting verkürzen

Große Netzwerk-Nutzlasten können sich zwar stark auf unsere App auswirken, aber es gibt noch etwas anderes, das einen wirklich großen Einfluss haben kann: JavaScript.

JavaScript ist Ihr teuerstes Asset. Wenn Sie auf Mobilgeräten große JavaScript-Pakete senden, kann sich verzögern, wie schnell Nutzer mit Ihren UI-Komponenten interagieren können. Das bedeutet, dass sie auf die Benutzeroberfläche tippen können, ohne dass tatsächlich etwas passiert. Deshalb ist es wichtig, dass wir verstehen, warum JavaScript so viel kostet.

So verarbeitet ein Browser JavaScript.

JavaScript-Verarbeitung
Abbildung 10. JavaScript-Verarbeitung

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

Auf einem High-End-Gerät wie einem Computer, einem Laptop oder sogar einem High-End-Smartphone dauern diese Phasen nicht sehr lange. Auf einem durchschnittlichen Smartphone kann dieser Vorgang jedoch fünf- bis zehnmal länger dauern. Dadurch wird die Interaktivität verzögert. Wir müssen also versuchen, diese Zeit zu verkürzen.

Damit Sie diese Probleme in Ihrer App leichter erkennen können, haben wir Lighthouse einen neuen Audit zur JavaScript-Boot-up-Zeit hinzugefügt.

JavaScript-Bootzeit
Abbildung 11. JavaScript-Audit zur Startzeit

Im Fall der Oodle-App wurde uns mitgeteilt, dass wir 1,8 Sekunden für das Booten von JavaScript benötigt haben. Wir haben alle unsere Routen und Komponenten statisch in ein monolithisches JavaScript-Bundle importiert.

Eine Möglichkeit, dieses Problem zu umgehen, ist die Verwendung von Code-Splitting.

Code-Splitting ist wie Pizza

Beim Code-Splitting wird den Nutzern nicht die gesamte JavaScript-Pizza auf einmal serviert, sondern nur ein Stück nach dem anderen, wenn sie es benötigen.

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

Wir haben Code-Splitting in unsere Anwendung integriert und von statischen zu dynamischen Importen gewechselt. So können wir Code asynchron nach Bedarf lazy-loaden.

Code-Splitting mit dynamischen Importen
Abb. 13. Code-Splitting mit dynamischen Importen

Dadurch konnten wir sowohl die Größe unserer Bundles als auch die JavaScript-Boot-up-Zeit verringern. Die Zeit wurde auf 0,78 Sekunden reduziert, wodurch die App 56% schneller wurde.

Wenn Sie eine JavaScript-lastige Anwendung entwickeln, sollten Sie dem Nutzer nur den Code senden, den er benötigt.

Nutzen Sie Konzepte wie Code-Splitting, probieren Sie Ideen wie Tree Shaking aus und sehen Sie sich das Repository webpack-libs-optimizations an. Dort finden Sie einige Ideen, wie Sie die Größe Ihrer Bibliothek reduzieren können, wenn Sie Webpack verwenden.

Bilder optimieren

Witz über die Ladeleistung von Bildern

In der Oodle-App verwenden wir viele Bilder. Leider war Lighthouse davon nicht so begeistert wie wir. Tatsächlich haben wir alle drei bildbezogenen Prüfungen nicht bestanden.

Wir haben vergessen, unsere Bilder zu optimieren, wir haben sie nicht richtig skaliert und wir könnten auch von der Verwendung anderer Bildformate profitieren.

Bild-Audits
Abb. 14. Lighthouse-Bildprüfungen

Wir haben damit begonnen, unsere Bilder zu optimieren.

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

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

So sorgen Sie dafür, dass die in Zukunft hinzugefügten Bilder automatisch optimiert werden. 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 auf diesen Diensten hosten.

Wenn Sie das aus Kostengründen oder wegen Latenzproblemen nicht möchten, bieten Projekte wie Thumbor oder Imageflow selbst gehostete Alternativen.

Vor und nach der Optimierung
Abbildung 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 angepasst und ImageOptim ausgeführt hatten, war es nur noch 100 KB groß, was akzeptabel ist.

Durch die Wiederholung dieses Vorgangs für mehrere Bilder auf unserer Website konnten wir das Gesamtgewicht der Seite deutlich reduzieren.

Das richtige Format für animierte Inhalte verwenden

GIFs können sehr teuer sein. Überraschenderweise war das GIF-Format ursprünglich nicht als Animationsplattform gedacht. Wenn Sie zu einem geeigneteren Videoformat wechseln, können Sie 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. Unser Clip hatte ein Gewicht von etwa 7, 3 MB, was für eine Website viel zu viel ist.Daher 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
Abbildung 16. Animierte GIFs durch Videos ersetzen

Wir haben das Tool FFmpeg verwendet, um unser Animations-GIF in die MP4-Datei zu konvertieren. Das WebM-Format bietet Ihnen noch größere Einsparungen. Die ImageOptim API kann die 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% unseres Gesamtgewichts einsparen. Dadurch konnten wir die Größe auf etwa 1 MB reduzieren.

1 MB ist jedoch eine große Ressource, die über das Netzwerk übertragen werden muss, insbesondere für Nutzer mit eingeschränkter Bandbreite. Glücklicherweise konnten wir die Effective Type API verwenden, um zu erkennen, dass die Bandbreite gering ist, und ihnen stattdessen ein viel kleineres JPEG senden.

Diese Schnittstelle verwendet die effektive Round-Trip-Zeit und die Downlink-Werte, 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önnten wir das Videoelement durch das Bild ersetzen, wenn der Nutzer eine Verbindung unter 4G hat.

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

Das schränkt die Nutzerfreundlichkeit zwar etwas ein, aber zumindest ist die Website auch bei einer langsamen Verbindung nutzbar.

Lazy Loading von nicht sichtbaren Bildern

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

Lighthouse kennzeichnet dieses Verhalten im Audit „Bilder außerhalb des sichtbaren Bereichs“ und Sie können es auch selbst im Netzwerkbereich der Entwicklertools sehen. Wenn viele Bilder eingehen, aber nur wenige auf der Seite sichtbar sind, sollten Sie in Erwägung ziehen, sie stattdessen verzögert zu laden.

Lazy Loading wird noch nicht nativ im Browser unterstützt. Daher müssen wir JavaScript verwenden, um diese Funktion hinzuzufügen. Wir haben die Lazysizes-Bibliothek verwendet, um das Lazy Loading-Verhalten für unsere Oodle-Cover 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 eine optimale Nutzerfreundlichkeit zu erzielen. Außerdem kann optional IntersectionObserver eingebunden werden, was sehr effiziente Sichtbarkeitsabfragen ermöglicht.

Nach dieser Änderung werden unsere Bilder bei Bedarf abgerufen. Wenn Sie sich näher mit diesem Thema befassen möchten, empfehlen wir Ihnen images.guide – eine sehr praktische und umfassende Ressource.

Browser dabei unterstützen, wichtige Ressourcen frühzeitig zu liefern

Nicht jedes Byte, das über das Netzwerk an den Browser gesendet wird, ist gleich wichtig. Das weiß der Browser. Viele Browser verwenden Heuristiken, um zu entscheiden, was zuerst abgerufen werden soll. Manchmal werden CSS-Dateien 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 die Webplattform gebracht wurden, helfen dem Browser, das Richtige zur richtigen Zeit abzurufen. Sie können etwas effizienter sein als einige der benutzerdefinierten, logikbasierten Ansätze, die mit Skripten umgesetzt werden.

Sehen wir uns an, wie Lighthouse uns bei der effektiven Nutzung einiger dieser Funktionen unterstützt.

Als Erstes rät uns Lighthouse, mehrere kostspielige Roundtrips zu einem Ursprung zu vermeiden.

Mehrere kostspielige Hin- und Rückfahrten zu einem beliebigen Ursprungsort vermeiden
Abbildung 17. Mehrere kostspielige Hin- und Rückfahrten zu einem beliebigen Ursprungsort vermeiden

Im Fall der Oodle-App verwenden wir Google Fonts sehr intensiv. Wenn Sie ein Google Fonts-Stylesheet in Ihre Seite einfügen, wird eine Verbindung zu bis zu zwei Subdomains hergestellt. Lighthouse weist uns darauf hin, dass wir durch das Aufwärmen der Verbindung bis zu 300 Millisekunden bei der anfänglichen Verbindungszeit sparen könnten.

Durch die Nutzung von „link rel=preconnect“ können wir diese Verbindungslatenz effektiv maskieren.

Das kann sich vor allem bei Google Fonts, wo unser CSS für Schriftarten auf googleapis.com und unsere Schriftartenressourcen auf Gstatic gehostet werden, sehr stark auswirken. Wir haben diese Optimierung also angewendet und konnten so einige Hundert Millisekunden einsparen.

Als Nächstes schlägt Lighthouse vor, wichtige Anfragen vorzuladen.

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

<link rel=preload> ist sehr leistungsstark. Es informiert den Browser, dass eine Ressource als Teil der aktuellen Navigation benötigt wird, und versucht, den Browser so schnell wie möglich zum Abrufen zu veranlassen.

Lighthouse weist uns darauf hin, dass wir unsere wichtigsten Webschriftartressourcen vorab laden sollten, da wir zwei Webschriftarten laden.

So sieht das Vorladen einer Web-Schriftart 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

Normalerweise muss der Browser, wenn Webschriftarten für Ihre Seite wichtig sind, ohne Verwendung von „link rel preload“ zuerst Ihr HTML abrufen, Ihr CSS parsen und erst viel später Ihre Webschriftarten abrufen.

Wenn Sie „link rel preload“ verwenden, kann der Browser mit dem Abrufen dieser Webfonts viel früher beginnen, sobald er Ihr HTML geparst hat. Im Fall unserer App konnten wir so die Zeit, die zum Rendern von Text mit unseren Webfonts benötigt wurde, um eine Sekunde verkürzen.

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

Die Google Font-URLs, die wir in unseren Stylesheets für unsere Schriftarten angeben, werden vom Schriftartenteam regelmäßig aktualisiert. Diese URLs können ablaufen oder regelmäßig aktualisiert werden. Wenn Sie also die vollständige Kontrolle über das Laden von Schriftarten haben möchten, empfehlen wir, Ihre Webfonts selbst zu hosten. Das kann sehr nützlich sein, da Sie dann beispielsweise auf das Link-Attribut „rel=preload“ zugreifen können.

In unserem Fall hat uns das Tool Google Web Fonts Helper sehr geholfen, einige dieser Webfonts offline zu nehmen und lokal einzurichten. Sehen Sie sich das Tool also einmal an.

Unabhängig davon, ob Sie Webfonts als Teil Ihrer wichtigen Ressourcen verwenden oder es sich um JavaScript handelt, sollten Sie dem Browser helfen, Ihre wichtigen Ressourcen so schnell wie möglich bereitzustellen.

Testversion: Prioritätshinweise

Wir haben heute etwas Besonderes für dich. Neben Funktionen wie Ressourcenhinweisen und Preload haben wir an einer brandneuen experimentellen Browserfunktion gearbeitet, die wir Prioritätshinweise nennen.

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

Mit dieser neuen Funktion können Sie dem Browser mitteilen, wie wichtig eine Ressource ist. Dazu wird ein neues Attribut namens „importance“ mit den Werten „low“, „high“ oder „auto“ eingeführt.

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 Elemente wie Hero-Bilder erhöhen.

Im Fall unserer Oodle-App hat dies tatsächlich zu einer praktischen Stelle geführt, an der wir optimieren konnten.

Priorität für die anfänglich sichtbaren Inhalte festlegen
Abbildung 21. Priorität für die anfangs sichtbaren Inhalte festlegen

Bevor wir Lazy Loading für unsere Bilder eingeführt haben, hat der Browser Folgendes getan: Wir hatten dieses Bildkarussell mit allen unseren Doodles und der Browser hat alle Bilder ganz am Anfang des Karussells mit hoher Priorität abgerufen. Leider waren die Bilder in der Mitte des Karussells für den Nutzer am wichtigsten. Wir haben also die Wichtigkeit dieser Hintergrundbilder auf sehr niedrig und die der Vordergrundbilder auf sehr hoch gesetzt. Das hatte zur Folge, dass wir bei einer langsamen 3G-Verbindung zwei Sekunden schneller in der Lage waren, diese Bilder abzurufen und zu rendern. Also eine schöne positive Erfahrung.

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

Strategie zum Laden von Webschriftarten

Typografie ist ein wichtiger Bestandteil von gutem Design. Wenn Sie Webfonts verwenden, sollten Sie das Rendern von Text idealerweise nicht blockieren und auf keinen Fall unsichtbaren Text anzeigen.

Wir weisen jetzt in Lighthouse mit dem Unsichtbaren Text vermeiden, während Webfonts geladen werden-Audit darauf hin.

Unsichtbaren Text vermeiden, während Webfonts geladen werden
Abbildung 22. Unsichtbaren Text vermeiden, während Webfonts geladen werden

Wenn Sie Ihre Webschriftarten mit einem Font-Face-Block laden, überlassen Sie es dem Browser, was er tun soll, 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 wurde, wird sie dann verwendet.

Wir versuchen, diesen unsichtbaren Text zu vermeiden. In diesem Fall hätten wir die klassischen Doodles dieser Woche nicht sehen können, wenn die Web-Schriftart zu lange geladen 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 Schriftartanzeige können Sie festlegen, wie Webfonts gerendert oder zurückgesetzt werden, je nachdem, wie lange es dauert, bis sie ersetzt werden.

In diesem Fall verwenden wir „font-display: swap“. Bei „swap“ hat die Schriftart einen Blockzeitraum von null Sekunden und einen unendlichen Swap-Zeitraum. Das bedeutet, dass der Browser Ihren Text ziemlich schnell mit einer Ersatzschriftart rendert, wenn das Laden der Schriftart länger dauert. Und es wird ausgetauscht, sobald die Schriftart verfügbar ist.

Im Fall unserer App war das von Vorteil, weil wir so schon sehr früh aussagekräftigen Text anzeigen und dann zur Web-Schriftart wechseln konnten, sobald sie bereit war.

Ergebnis der Schriftart-Anzeige
Abbildung 23. Ergebnis der Schriftart-Anzeige

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

Es gibt viele Webplattformfunktionen, mit denen Sie das Laden von Schriftarten optimieren können. Sehen Sie sich auch das Web Font Recipes-Repository von Zach Leatherman an, da es wirklich großartig ist.

Scripts reduzieren, die das Rendern blockieren

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

Im Zeitachsenstreifen von Lighthouse 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
Abb. 24. Möglichkeit, Stylesheets zu reduzieren, 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 für dieses erste Rendern verantwortlichen Stile extrahieren und in unser HTML 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 einzufügen.

Dieses Modul hat uns zwar viel Arbeit abgenommen, aber es war trotzdem etwas schwierig, es für verschiedene Routen reibungslos zum Laufen zu bringen.

Wenn Sie nicht vorsichtig sind oder Ihre Website-Struktur sehr komplex ist, kann es sehr schwierig sein, dieses Muster einzuführen, wenn Sie die App-Shell-Architektur nicht von Anfang an geplant haben.

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

Am Ende hat sich das Risiko ausgezahlt. Wir haben es geschafft, die App so zu optimieren, dass Inhalte viel früher geladen werden und sich die Zeit bis zum ersten Rendern von Inhalten deutlich verkürzt 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 auf einem mittelgroßen Mobilgerät in einem 3G-Netzwerk geladen, vor und nach der Optimierung.

Die Lighthouse-Leistungsbewertung 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 Einhaltung des Lighthouse-Berichts vorangetrieben. Wenn Sie sich ansehen möchten, wie wir die Verbesserungen technisch umgesetzt haben, können Sie sich unser Repository ansehen, insbesondere die dort eingegangenen Pull Requests.

Vorhersageleistung – datengesteuerte Nutzererfahrung

Wir sind der Meinung, dass maschinelles Lernen in vielen Bereichen eine spannende Möglichkeit für die Zukunft darstellt. Eine Idee, die hoffentlich zu mehr Experimenten anregen wird, ist, dass echte Daten wirklich die Nutzererlebnisse leiten können, die wir schaffen.

Derzeit 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 vorab im Cache gespeichert werden sollte. Wenn wir richtig raten, können wir eine kleine Menge von Ressourcen priorisieren, aber es ist wirklich schwierig, das auf die gesamte Website zu übertragen.

Wir haben jetzt Daten, die uns bei der Optimierung helfen. Mit der Google Analytics Reporting API können wir uns die nächsten Top-Seiten und die prozentualen Anteile der Ausstiege für jede URL auf unserer Website ansehen und so Rückschlüsse darauf ziehen, welche Ressourcen wir priorisieren sollten.

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

Datengestützte Bündelung für Web-Apps
Abbildung 25. Datengestützte Bündelung für Web-Apps

Um diese Tests zu erleichtern, haben wir eine neue Initiative namens Guess.js ins Leben gerufen.

Guess.js
Abbildung 26. Guess.js

Guess.js ist ein Projekt, das sich auf datengesteuerte Nutzererlebnisse für das Web konzentriert. Wir hoffen, dass Sie dadurch angeregt werden, Daten zu nutzen, um die Webleistung zu verbessern und noch mehr zu erreichen. Die Software ist Open Source und auf GitHub verfügbar. Diese Funktion wurde in Zusammenarbeit mit der Open-Source-Community von Minko Gechev, Kyle Matthews von Gatsby, Katie Hempenius und vielen 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. Sie sind jedoch nur das Mittel zum Zweck, nicht das Ziel selbst.

Wir alle haben schon einmal erlebt, dass Seiten unterwegs langsam geladen werden. Jetzt haben wir die Möglichkeit, unseren Nutzern eine bessere Nutzerfreundlichkeit zu bieten, indem wir dafür sorgen, dass Seiten schnell geladen werden.

Die Leistung zu verbessern ist ein fortlaufender Prozess. Viele kleine Änderungen können zu großen Verbesserungen führen. Mit den richtigen Optimierungstools und einem Blick auf die Lighthouse-Berichte können Sie Ihren Nutzern eine bessere und inklusivere Nutzererfahrung bieten.

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