Webleistung leicht gemacht – Google I/O 2018

Bei der Google IO 2018 haben wir eine Reihe von Tools, Bibliotheken und Optimierungstechniken vorgestellt, mit denen sich die Webleistung einfacher verbessern lässt. Hier erklären wir sie anhand der App „Oodles Theater“. Außerdem sprechen wir über unsere Tests mit dem vorausschauenden Laden und die neue Initiative „Guess.js“.

Addy Osmani
Addy Osmani
Ewa Gasperowicz

Im letzten Jahr haben wir uns intensiv damit beschäftigt, das Web schneller und leistungsfähiger zu machen. Das hat zu neuen Tools, Ansätzen und Bibliotheken geführt, die wir Ihnen in diesem Artikel vorstellen möchten. Im ersten Teil zeigen wir Ihnen einige Optimierungstechniken, die wir bei der Entwicklung der Oodles Theater App eingesetzt haben. Im zweiten Teil sprechen wir über unsere Tests mit dem vorausschauenden Laden und die neue Initiative Guess.js.

Die Notwendigkeit von Leistung

Das Internet wird von Jahr zu Jahr immer intensiver. Wenn wir uns den Status des Webs ansehen, stellen wir fest, dass der Medianwert für die Seite auf Mobilgeräten bei etwa 1,5 MB liegt.Dabei handelt es sich zum größten Teil um JavaScript und Bilder.

Die wachsende Größe der Websites trägt zusammen mit anderen Faktoren wie Netzwerklatenz, CPU-Einschränkungen, Renderblockierungsmustern oder überflüssigem Drittanbietercode zum komplexen Leistungspuzzle bei.

Die meisten Nutzer bewerten die Geschwindigkeit ganz oben in der UX-Hierarchie ihrer Anforderungen. Das ist nicht weiter verwunderlich, da Sie nicht viel tun können, bis eine Seite vollständig geladen ist. Sie können keinen Mehrwert aus der Seite ableiten und ihre Ästhetik nicht bewundern.

UX-Hierarchie-Piramid
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 aber auch ein Rätsel sein, wo Sie mit der Optimierung beginnen sollen. Zum Glück gibt es Tools, die Ihnen dabei helfen können.

Lighthouse – eine Grundlage für den Leistungs-Workflow

Lighthouse ist ein Teil der Chrome-Entwicklertools, mit dem Sie Ihre Website prüfen und Hinweise zur Optimierung erhalten können.

Vor Kurzem haben wir eine Reihe von neuen Leistungsanalysen 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, wie Sie sie anhand eines praktischen Beispiels nutzen können: Die Oodles Theater App. Das 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 wollten wir dafür sorgen, dass sie möglichst leistungsfähig 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 App interaktiv wurde. Lighthouse hat eine Menge Probleme mit unserer Website aufgezeigt und die Gesamtbewertung der Leistung von 23 spiegelt genau das wider.

Die Seite wog etwa 3,4 MB. Wir mussten dringend etwas optimieren.

Das war der Beginn unserer ersten Leistungsherausforderung: Dinge finden, die wir einfach entfernen können, ohne die Gesamtnutzung zu beeinträchtigen.

Leistungsoptimierung

Unnötige Ressourcen entfernen

Es gibt einige offensichtliche Dinge, die Sie entfernen können: Leerzeichen und Kommentare.

Vorteile der Minimierung
Abbildung 4 JavaScript und CSS minimieren und komprimieren

Lighthouse hebt diese Möglichkeit in der Prüfung unminierter CSS- und JavaScript-Dateien hervor. Wir haben webpack für unseren Build-Prozess verwendet. Für die Minimierung haben wir einfach das Uglify JS-Plug-in verwendet.

Die Minimierung ist eine gängige Aufgabe. Daher sollten Sie für jeden Buildprozess, 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. Die meisten CDNs unterstützen dies bereits standardmäßig.

Wir haben Firebase Hosting verwendet, um unseren Code zu hosten, und Firebase aktiviert gzip standardmäßig, sodass wir unseren Code auf einem vernünftigen CDN hosten können, den wir kostenlos erhalten haben.

Gzip ist eine sehr beliebte Komprimierungsmethode, aber auch andere Mechanismen wie Zopfli und Brotli gewinnen an Bedeutung. Brotli wird von den meisten Browsern unterstützt und Sie können Ihre Assets mit einem Binärprogramm vorab komprimieren, bevor Sie sie an den Server senden.

Effiziente Cache-Richtlinien verwenden

Im nächsten Schritt wollten wir dafür sorgen, dass wir Ressourcen nicht unnötig doppelt 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 einer Max-Age-Header-Gültigkeitsdauer auf unserem Server haben wir dafür gesorgt, dass Nutzer bei einem wiederholten Besuch die zuvor heruntergeladenen Ressourcen wiederverwenden können.

Idealerweise sollten Sie versuchen, so viele Ressourcen wie möglich über den längstmöglichen Zeitraum im Cache zu speichern, und Validierungstokens für eine effiziente Neuvalidierung der aktualisierten Ressourcen bereitstellen.

Entfernen Sie nicht verwendeten Code.

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

Codeabdeckung in den Entwicklertools
Abb. 5. Codeabdeckung prüfen

Manchmal fügen wir in unseren App-Code Code ein, der nicht wirklich notwendig ist. Das passiert vor allem, wenn Sie längere Zeit an Ihrer App arbeiten, sich Ihr Team oder Ihre Abhängigkeiten ändern und manchmal eine Bibliothek ohne Elternelement übrig bleibt. Genau das ist uns passiert.

Anfangs haben wir die Material Components-Bibliothek verwendet, um schnell einen Prototyp für unsere App zu erstellen. Nach und nach haben wir das Design angepasst und die Bibliothek ganz vergessen. Dank der Überprüfung der Codeabdeckung konnten wir sie in unserem Paket wiederfinden.

Sie können die Statistiken zur Codeabdeckung in den Entwicklertools überprüfen, sowohl für die Laufzeit als auch für die Ladezeit Ihrer Anwendung. 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 bei der Prüfung auf nicht verwendete CSS-Regeln erkannt. Es wurde eine potenzielle Einsparung von über 400 KB angezeigt. Wir sind also zu unserem Code zurückgekehrt und haben sowohl den JavaScript- als auch den CSS-Teil dieser Bibliothek entfernt.

Wenn wir den MVC-Adapter entfernen, sinken unsere Stile auf 10 KB.
Abb. 6. Wenn wir den MVC-Adapter veröffentlichen, fallen unsere Stile auf 10 KB.

Dadurch wurde unser CSS-Bundle um das 20-fache verkleinert, was für ein kleines, zweizeiliges Commit ziemlich gut ist.

Dadurch konnte unser Leistungswert natürlich gesteigert werden und auch die Time to Interactive wurde deutlich verbessert.

Bei solchen Änderungen reicht es jedoch nicht aus, nur die Messwerte und Bewertungen zu prüfen. Das Entfernen des eigentlichen Codes ist nie risikofrei. Achten Sie daher immer auf potenzielle Regressionen.

Unser Code wurde in 95 % der Fälle nicht verwendet, aber es gibt immer noch diese 5 %. Offenbar wurden für eine unserer Komponenten noch die Stile aus dieser Bibliothek verwendet – die kleinen Pfeile im Doodlet-Schieberegler. Da es aber so klein war, konnten wir diese Stile einfach manuell wieder in die Schaltflächen einbinden.

Schaltflächen funktionieren nicht, weil die Bibliothek fehlt
Abbildung 7 Eine Komponente verwendete weiterhin die entfernte Bibliothek.

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

Sehr große Netzwerknutzlasten vermeiden

Uns ist bewusst, dass große Ressourcen das Laden von Webseiten verlangsamen können. Sie können unsere Nutzer Geld kosten und einen großen Einfluss auf ihre Datentarife haben. Daher ist es sehr wichtig, dies im Hinterkopf zu behalten.

Lighthouse hat mithilfe des Audits Enorme Netzwerknutzlast ein Problem mit einigen unserer Netzwerknutzlasten erkannt.

Erkennen von sehr großen Netzwerknutzlasten
Abbildung 8 Erkennen von sehr großen Netzwerknutzlasten

Hier wurden mehr als 3 MB an Code versendet – das ist ziemlich viel, vor allem auf Mobilgeräten.

Ganz oben in dieser Liste wies Lighthouse darauf hin, dass wir ein JavaScript-Anbieter-Bundle mit 2 MB unkomprimierten Code hatten. Dieses Problem wird auch von Webpack hervorgehoben.

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 ursprünglichen Version zu veröffentlichen. Weil diese Assets manchmal verzögert oder verzögert geladen oder während der Inaktivität verarbeitet werden können.

Da wir es mit vielen JavaScript-Bundles zu tun hatten, hatten wir Glück, denn die JavaScript-Community bietet eine Vielzahl von Tools zur Analyse von JavaScript-Bundles.

JavaScript-Bundle-Audit
Abbildung 9. Prüfung des JavaScript-Bundles

Wir haben mit dem Webpack-Bundle-Analyzer begonnen, der uns mitteilte, dass wir eine Abhängigkeit namens „unicode“ mit 1,6 MB geparsten JavaScripts einschließen, was ziemlich viel ist.

Anschließend haben wir den Editor geöffnet und mit dem Import Cost Plugin for Visual Code die Kosten jedes Moduls visualisiert, das wir importiert haben. So konnten wir feststellen, welche Komponente Code enthielt, der auf dieses Modul verweist.

Wir sind dann zu einem anderen Tool gewechselt, BundlePhobia. Mit diesem Tool können Sie den Namen eines NPM-Pakets eingeben und sehen, wie groß die reduzierte und mit gzip komprimierte Größe ist. Wir haben für das verwendete Slug-Modul eine gute Alternative gefunden, die nur 2,2 KB wog.

Das hatte großen Einfluss auf unsere Leistung. Zwischen dieser Änderung und der Entwicklung weiterer Möglichkeiten zum Reduzieren der Größe unseres JavaScript-Pakets haben wir 2, 1 MB Code eingespart.

Insgesamt konnten wir eine Verbesserung von 65 % erzielen, wenn man die Größe der gezippten und minimierten Bundles berücksichtigt. Und wir haben festgestellt, dass sich dieser Prozess wirklich lohnt.

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

JavaScript-Startzeit mit Code-Splitting verkürzen

Große Netzwerknutzlasten können zwar einen großen Einfluss auf unsere App haben, 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-Bundles senden, kann es zu Verzögerungen bei der Interaktion der Nutzer mit den Komponenten der Benutzeroberfläche kommen. Das bedeutet, dass sie auf die Benutzeroberfläche tippen können, ohne dass etwas Sinnvolles passiert. Daher ist es wichtig, dass wir verstehen, warum JavaScript so viel kostet.

So wird JavaScript von einem Browser verarbeitet.

JavaScript-Verarbeitung
Abbildung 10 JavaScript-Verarbeitung

Wir müssen zuerst dieses Script herunterladen. Wir haben eine JavaScript-Engine, die diesen Code dann parsen, kompilieren und ausführen muss.

Auf einem High-End-Gerät wie einem Desktop-Computer oder Laptop, vielleicht 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. Das verzögert die Interaktivität. Wir sollten versuchen, das zu reduzieren.

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

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

Bei der App „Oodle“ gab es an, dass der JavaScript-Start 1,8 Sekunden dauerte. Wir importierten alle unsere Routen und Komponenten statisch in ein monolithisches JavaScript-Bundle.

Eine Methode zur Umgehung dieses Problems ist die Codeaufteilung.

Code-Splitting ist wie Pizza

Beim Code-Splitting wird der JavaScript-Code nicht auf einmal, sondern nach Bedarf in mehreren Teilen an die Nutzer gesendet.

Die Codeaufteilung 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 eingebunden und von statischen zu dynamischen Importen gewechselt, sodass wir Code nach Bedarf asynchron lazy laden konnten.

Code-Splitting mit dynamischen Importen
Abbildung 13. Codesplitting mit dynamischen Importen

Dadurch konnten wir nicht nur die Größe unserer Bundles, sondern auch die JavaScript-Startzeit verkürzen. Es dauerte nur 0,78 Sekunden, was die App 56% schneller machte.

Wenn Sie eine JavaScript-lastige Website erstellen, sollten Sie den Nutzern nur den Code senden, den sie benötigen.

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

Bilder optimieren

Witz beim Laden von Bildern

In der Oodle App verwenden wir sehr viele Bilder. Leider war Lighthouse viel weniger begeistert als wir. Tatsächlich sind wir bei allen drei Prüfungen in Bezug auf Bilder nicht bestanden.

Wir haben vergessen, die Bilder zu optimieren, da sie nicht richtig dimensioniert wurden. Außerdem könnten wir durch die Verwendung anderer Bildformate Geld verdienen.

Bildüberprüfungen
Abbildung 14. Lighthouse-Bildprüfungen

Wir haben damit begonnen, unsere Bilder zu optimieren.

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

Ein automatisierterer Ansatz besteht darin, Ihrem Build-Prozess mithilfe von 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 Lösungen von Drittanbietern 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
Abbildung 15. Vorher und nachher

Unsere Hintergrund-PNG-Datei wurde im Webpack als groß markiert. Nach der Größenanpassung für den Darstellungsbereich und der Ausführung über ImageOptim sind wir auf 100 KB reduziert, was akzeptabel ist.

Dadurch, dass wir dies für mehrere Bilder auf unserer Website wiederholt haben, konnten wir die Gesamtseitengröße 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. Daher können Sie mit einem Wechsel zu einem geeigneteren Videoformat große Einsparungen bei der Dateigröße erzielen.

In der Oodle-App verwendeten wir ein GIF als Intro-Sequenz auf der Startseite. Laut Lighthouse könnten wir über 7 MB sparen, wenn wir zu einem effizienteren Videoformat wechseln. Unser Clip wog etwa 7,3 MB, was für eine vernünftige 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
Abbildung 16. Animierte GIFs durch Video ersetzen

Wir haben unser Animations-GIF mit dem Tool FFmpeg in die MP4-Datei konvertiert. Mit dem WebM-Format können Sie noch größere Einsparungen erzielen – die ImageOptim API kann eine solche Konvertierung für Sie vornehmen.

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 sind wir auf etwa 1 MB reduziert.

1 MB ist jedoch eine große Ressource, die über das Netzwerk gesendet werden muss, insbesondere für Nutzer mit eingeschränkter Bandbreite. Glücklicherweise konnten wir mithilfe der Effective Type API feststellen, dass die Bandbreite des Nutzers niedrig ist, und ihm stattdessen ein viel kleineres JPEG senden.

Bei dieser Schnittstelle werden die effektive Laufzeit und die Ausfallwerte verwendet, um den vom Nutzer verwendeten Netzwerktyp zu schätzen. Sie gibt einfach einen String zurück: langsames 2G, 2G, 3G oder 4G. Je nach diesem Wert können wir das Videoelement durch ein Bild ersetzen, wenn der Nutzer eine Verbindung mit weniger als 4G hat.

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

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

Nicht sichtbare Bilder per Lazy Loading laden

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

Lighthouse markiert dieses Verhalten bei der Prüfung von Bildern außerhalb des Bildschirms. Außerdem kannst du es dir im Bereich „Netzwerk“ der Entwicklertools ansehen. Wenn viele Bilder ankommen, aber nur wenige auf der Seite zu sehen 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 den Oodle-Covern ein Lazy Loading-Verhalten 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, da es nicht nur die Sichtbarkeitsänderungen des Elements erfasst, sondern auch proaktiv Elemente im Voraus lädt, die sich in der Nähe des Darstellungsbereichs befinden, um die Nutzerfreundlichkeit zu optimieren. Außerdem bietet er eine optionale Einbindung der IntersectionObserver, mit der Sie sehr effiziente Sichtbarkeitsabfragen durchführen können.

Nach dieser Änderung werden unsere Bilder auf Anfrage abgerufen. Weitere Informationen zu diesem Thema finden Sie unter images.guide.

Browser dabei unterstützen, wichtige Ressourcen frühzeitig bereitzustellen

Nicht jedes Byte, das über die Leitung an den Browser gesendet wird, ist gleich wichtig. Der Browser weiß das. Viele Browser verwenden Heuristiken, um zu entscheiden, was zuerst abgerufen werden soll. Manchmal wird also CSS vor Bildern oder Scripts abgerufen.

Es könnte hilfreich sein, wenn wir als Autoren der Seite dem Browser mitteilen, was für 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 verfügbar sind, helfen dem Browser, das richtige Element zur richtigen Zeit abzurufen. Sie können auch etwas effizienter sein als einige der logischbasierten benutzerdefinierten Ladeansätze, die stattdessen über ein Skript erfolgen.

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

Als Erstes wird in Lighthouse empfohlen, mehrere kostenintensive Rück- und Rückfahrten zu einem beliebigen Ursprung zu vermeiden.

Mehrere, kostspielige Hin- und Rückflüge zu beliebigen Startorten vermeiden
Abb. 17. Vermeiden Sie mehrere, kostenintensive Hin- und Rückfahrten zu einem beliebigen Startpunkt.

Im Fall der Oodle-App verwenden wir sogar sehr häufig Google Fonts. Wenn Sie ein Google Font-Stylesheet auf Ihrer Seite einfügen, werden damit bis zu zwei Subdomains verbunden. Lighthouse zeigt uns, dass wir bei einer Aufwärmung der Verbindung bis zu 300 Millisekunden bei der anfänglichen Verbindungszeit einsparen könnten.

Mithilfe von „link rel preconnect“ können wir diese Verbindungslatenz effektiv verbergen.

Das kann besonders bei Google Fonts eine große Auswirkung haben, da unsere CSS-Schriftschnitte auf googleapis.com und unsere Schriftressourcen auf Gstatic gehostet werden. Wir haben diese Optimierung angewendet und konnten so einige hundert Millisekunden einsparen.

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

Wichtige Anfragen vorab laden
Abb. 18. Wichtige Anfragen vorab laden

<link rel=preload> ist sehr leistungsfähig. Es informiert den Browser darüber, dass eine Ressource im Rahmen der aktuellen Navigation benötigt wird, und versucht, den Browser dazu zu bringen, sie so schnell wie möglich abzurufen.

Lighthouse teilt uns hier mit, dass wir unsere wichtigsten Webschriftarten-Ressourcen vorab laden sollten, da wir zwei Webschriften laden.

Das Vorladen eines Web-Fonts sieht so aus: Sie geben rel=preload an, übergeben as mit der Schriftart und geben dann die Schriftart an, die Sie laden möchten, z. B. woff2.

Das kann sich stark auf Ihre Seite auswirken.

Auswirkungen des Vorabladens von Ressourcen
Abbildung 19. Auswirkungen des Vorabladens von Ressourcen

Wenn Webschriftarten für eine Seite wichtig sind, muss der Browser normalerweise zuerst den HTML-Code abrufen, den CSS-Code parsen und schließlich Ihre Webschriftarten abrufen.

Mit „link rel preload“ kann der Browser diese Webfonts viel früher abrufen, sobald er Ihren HTML-Code geparst hat. Mit unserer App konnten wir damit die Zeit, die wir zum Rendern von Text mit unseren Webschriftarten benötigen, um eine Sekunde verkürzen.

Das Vorladen von Schriftarten mit Google Fonts ist nicht ganz so einfach, da es eine kleine Einschränkung gibt.

Die Google Fonts-URLs, die wir in unseren Stylesheets für unsere Schriftschnitte angeben, werden vom Fonts-Team ziemlich regelmäßig aktualisiert. Diese URLs können ablaufen oder regelmäßig aktualisiert werden. Wenn Sie das Laden von Schriftarten vollständig steuern möchten, sollten Sie Ihre Webfonts selbst hosten. Das kann sehr nützlich sein, da Sie so beispielsweise auf „link rel preload“ zugreifen können.

In unserem Fall hat uns das Tool Google Web Fonts Helper dabei geholfen, einige dieser Webfonts offline zu stellen und lokal einzurichten.

Unabhängig davon, ob Sie für Ihre wichtigen Ressourcen Webschriftarten oder JavaScript-Schriftarten verwenden, sollten Sie dafür sorgen, dass der Browser Ihre kritischen Ressourcen so schnell wie möglich bereitstellt.

Experimentell: Hinweise zur Priorität

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

Priorität für die anfangs sichtbaren Inhalte festlegen
Abbildung 20 Hinweise zur Priorität

Dies ist eine neue Funktion, mit der Sie dem Browser mitteilen können, wie wichtig eine Ressource ist. Sie stellt das neue Attribut „Wichtigkeit“ mit den Werten „Niedrig“, „Hoch“ oder „Automatisch“ bereit.

So können wir die Priorität weniger wichtiger Ressourcen wie nicht kritischer Stile, Bilder oder Abruf-API-Aufrufe senken, um Konflikte zu reduzieren. Wir können auch wichtigere Dinge priorisieren, wie unsere Hero-Images.

Im Fall unserer Oodle-App führte dies zu einer praktischen Stelle, an der wir Optimierungen vornehmen konnten.

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

Bevor wir Lazy Loading für unsere Bilder verwendet haben, hatten wir dieses Bilderkarussell 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 die Nutzer am wichtigsten. Wir haben also die Wichtigkeit dieser Hintergrundbilder auf sehr niedrig und die Vordergrundbilder auf sehr hoch gesetzt. Dies hatte eine Wirkung von zwei Sekunden bei langsamem 3G und wie schnell wir diese Bilder abrufen und rendern konnten. Eine tolle und positive Erfahrung.

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

Ladestrategie für Webschriftarten

Die Typografie ist für ein gutes Design unerlässlich. Wenn Sie Web-Schriftarten verwenden, sollten Sie das Rendering Ihres Texts nicht blockieren und auf keinen Fall unsichtbaren Text anzeigen.

Wir heben dies jetzt in Lighthouse hervor, um unsichtbaren Text während des Ladens von Webschriftarten zu vermeiden.

Unsichtbaren Text beim Laden von Webfonts vermeiden
Abb. 22. Vermeiden Sie unsichtbaren Text beim Laden von Webfonts

Wenn Sie Ihre Webschriftarten mit einem Font Face Block laden, lässt der Browser entscheiden, wie vorgegangen werden soll, wenn es lange dauert, die Webschriftart abzurufen. Einige Browser warten darauf bis zu drei Sekunden, bevor sie auf eine Systemschriftart zurückgreifen. Letztendlich wird sie nach dem Download in die Schriftart ausgetauscht.

Wir versuchen, diesen unsichtbaren Text zu vermeiden. In diesem Fall hätten wir die klassischen Doodles dieser Woche nicht sehen können, wenn der Webfont zu lange gedauert 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 Webschriften gerendert oder als Fallback verwendet werden, je nachdem, wie lange der Austausch dauert.

In diesem Fall verwenden wir die Schriftanzeige. Dadurch erhält die Schriftart eine Blockperiode von null Sekunden und einen unendlichen Austauschzeitraum. Das bedeutet, dass der Browser Ihren Text ziemlich sofort mit einer Fallback-Schriftart anzeigt, wenn das Laden der Schriftart etwas dauert. Sobald die Schriftart verfügbar ist, wird sie ersetzt.

Im Fall unserer App war das großartig, da wir so schon früh aussagekräftigen Text anzeigen und zur Webschriftart wechseln konnten, sobald sie fertig war.

Ergebnis der Schriftartanzeige
Abbildung 23. Ergebnis der Schriftanzeige

Wenn Sie wie ein großer Teil des Webs Webschriften verwenden, sollten Sie im Allgemeinen eine gute Strategie zum Laden von Webschriften haben.

Es gibt viele Funktionen der Webplattform, mit denen du das Laden von Schriftarten optimieren kannst, aber auch das Repository „Web Font Recipes“ von Zach Leatherman ist hervorragend.

Skripte reduzieren, die das Rendering blockieren

Es gibt andere Teile unserer Anwendung, die wir früher in der Downloadkette einschleusen könnten, um Nutzern schon etwas früher zumindest eine grundlegende Nutzererfahrung zu bieten.

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

Weniger Stylesheets, die das Rendering blockieren
Abbildung 24. Reduzierung der Anzahl von Stylesheets, die das Rendering blockieren

Das Herunterladen und Verarbeiten externer Stylesheets verhindert, dass der Rendering-Prozess fortgesetzt werden kann.

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

Wenn wir die Stile, die für dieses erste Rendern verantwortlich sind, extrahieren und in unsere HTML-Datei einfügen, kann der Browser sie sofort rendern, ohne auf die externen Stylesheets zu warten.

In unserem Fall haben wir ein NPM-Modul namens Critical verwendet, um unseren kritischen Inhalt während eines Build-Schritts in die Datei "index.html" einzubetten.

Dieses Modul hat zwar den Großteil der Arbeit für uns erledigt, es war aber trotzdem etwas schwierig, die Funktion für verschiedene Routen reibungslos zu implementieren.

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, frühzeitig die Leistung zu berücksichtigen. Wenn Sie nicht von Anfang an auf Leistung achten, besteht eine hohe Wahrscheinlichkeit, dass Sie später Probleme bekommen.

Letztendlich hat sich das Risiko gelohnt. Wir haben es geschafft, die App so zu optimieren, dass Inhalte viel früher ausgeliefert wurden. Dadurch konnte die Zeit bis zur ersten vollständigen Darstellung deutlich verkürzt werden.

Das Ergebnis

Das war eine lange Liste von Leistungsoptimierungen, die wir auf unsere Website angewendet 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.

Die Lighthouse-Leistungsbewertung stieg von 23 auf 91. Das ist ein ziemlicher Fortschritt in Sachen Geschwindigkeit. Als Grundlage für alle Änderungen haben wir den Lighthouse-Bericht ständig geprüft und berücksichtigt. Wenn du wissen möchtest, wie wir alle Verbesserungen technisch implementiert haben, sieh dir unser Repository an, insbesondere die PRs, die dort gelandet sind.

Prognoseleistung – datengetriebene Nutzererfahrung

Wir sind der Ansicht, dass maschinelles Lernen in vielen Bereichen eine spannende Zukunftstechnologie darstellt. Wir hoffen, dass wir in Zukunft mehr Experimente anregen können, weil wir wissen, dass echte Daten die Nutzererfahrung, die wir schaffen, wirklich beeinflussen können.

Heute treffen wir viele willkürliche Entscheidungen darüber, was Nutzer möglicherweise wünschen oder benötigen und was daher im Voraus abgerufen, vorab geladen oder vorab im Cache gespeichert werden sollte. Wenn wir richtig raten, können wir eine kleine Anzahl von Ressourcen priorisieren, aber es ist sehr schwierig, dies auf die gesamte Website auszuweiten.

Wir haben jetzt Daten, die unsere Optimierungen noch besser unterstützen. Mit der Google Analytics Reporting API können wir für jede URL auf unserer Website die nächste Top-Page- und Ausstiegsquote für jede URL abrufen 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 Vorab-Caching von Inhalten verschwendet werden. Wir können diese Google Analytics-Daten nutzen und maschinelles Lernen und Modelle wie Markov-Ketten oder neuronale Netze verwenden, um solche Modelle zu implementieren.

Datengestütztes Bündeln für Web-Apps
Abbildung 25. Datengestütztes Bündeln für Webanwendungen

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 User Experiences für das Web konzentriert. Wir hoffen, dass Sie die Daten nutzen, um die Webleistung zu verbessern und darüber hinauszugehen. Das gesamte System ist Open Source und bereits auf GitHub verfügbar. Er wurde in Zusammenarbeit mit der Open-Source-Community von Minko Gechev, Kyle Matthews von Gatsby, Katie Hempenius und vielen anderen erstellt.

Probieren Sie Guess.js aus und teilen Sie uns Ihre Meinung mit.

Zusammenfassung

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

Wir alle kennen das Problem langsamer Seitenladezeiten unterwegs. Jetzt haben wir die Möglichkeit, unseren Nutzern eine noch bessere Nutzererfahrung zu bieten, die wirklich schnell geladen wird.

Die Leistungssteigerung ist ein Prozess. Viele kleine Änderungen können zu großen Gewinnen führen. Wenn Sie die richtigen Optimierungstools verwenden und die Lighthouse-Berichte im Blick behalten, können Sie Ihren Nutzern eine bessere und inklusivere Nutzung 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.