Ressourcenauswahl mit Clienthinweisen automatisieren

Ilya Grigorik
Ilya Grigorik

Die Entwicklung für das Web bietet Ihnen eine unvergleichliche Reichweite. Ihre Webanwendung ist nur einen Klick entfernt und auf fast allen verbundenen Geräten verfügbar – Smartphones, Tablets, Laptops, Computern, Fernsehern und weiteren Geräten – unabhängig von der Marke oder Plattform. Sie haben eine responsive Website erstellt, die die Darstellung und Funktionalität für jeden Formfaktor anpasst. Jetzt geht es darum, die Leistung der Anwendung so schnell wie möglich zu optimieren: Sie haben Ihren kritischen Rendering-Pfad optimiert, Ihre Textressourcen komprimiert und im Cache gespeichert und jetzt sehen Sie sich die Bildressourcen an, die häufig übertragen werden. Das Problem ist, dass die Bildoptimierung schwierig ist:

  • Geeignetes Format bestimmen (Vektor- oder Rasterformat)
  • Die optimalen Codierungsformate ermitteln (JPEG, WEBP usw.)
  • Die richtigen Komprimierungseinstellungen festlegen (verlustbehaftet oder verlustfrei)
  • Festlegen, welche Metadaten beibehalten oder entfernt werden sollen
  • Für jedes Display und jede DPR-Auflösung mehrere Varianten erstellen
  • ...
  • Netzwerktyp, Geschwindigkeit und Einstellungen des Nutzers berücksichtigen

Im Einzelnen handelt es sich dabei um bekannte Probleme. Gemeinsam schaffen sie einen großen Optimierungsbereich, den wir (die Entwickler) häufig übersehen oder vernachlässigen. Menschen erforschen oft denselben Suchbereich nur schlecht, vor allem, wenn viele Schritte erforderlich sind. Computer hingegen eignen sich hervorragend für solche Aufgaben.

Die Antwort auf eine gute und nachhaltige Optimierungsstrategie für Bilder und andere Ressourcen mit ähnlichen Eigenschaften ist einfach: Automatisierung. Wenn Sie Ihre Ressourcen manuell anpassen, machen Sie es falsch: Sie werden es vergessen, Sie werden faul werden oder jemand anderes macht diesen Fehler für Sie – garantiert.

Die Saga des leistungsbewussten Entwicklers

Die Suche in der Bildoptimierung besteht aus zwei Phasen: Build-Zeit und Laufzeit.

  • Einige Optimierungen sind für die Ressource selbst unverzichtbar, z.B. die Auswahl des richtigen Formats und Codierungstyps, die Feinabstimmung der Komprimierungseinstellungen für jeden Encoder, das Entfernen unnötiger Metadaten usw. Diese Schritte können zur „Build-Zeit“ ausgeführt werden.
  • Weitere Optimierungen hängen vom Typ und den Eigenschaften des Clients ab, der sie anfordert, und müssen während der Laufzeit durchgeführt werden: Auswahl der geeigneten Ressource für den DPR des Kunden und die beabsichtigte Anzeigebreite, unter Berücksichtigung der Netzwerkgeschwindigkeit des Kunden, der Nutzer- und Anwendungseinstellungen usw.

Die Tools zur Erstellungszeit sind vorhanden, könnten aber verbessert werden. Beispielsweise lassen sich durch die dynamische Feinabstimmung der Einstellung „Qualität“ für jedes Bild und jedes Bildformat viele Einsparungen erzielen. Aber ich kann noch nicht feststellen, dass sie auch außerhalb der Forschung verwendet wird. Dies ist ein Bereich, der reif für Innovationen ist, aber in diesem Post möchte ich das belassen. Konzentrieren wir uns auf die Laufzeit.

<img src="/image/thing" sizes="50vw"
        alt="image thing displayed at 50% of viewport width">

Der Intent der Anwendung ist sehr einfach: Das Bild wird in 50% des Darstellungsbereichs des Nutzers abgerufen und angezeigt. Hier waschen sich fast alle Designschaffenden die Hände und Köpfe für die Bar. In der Zwischenzeit hat der leistungsbewusste Entwickler im Team eine lange Nacht hinter sich:

  1. Für eine optimale Komprimierung sollte für jeden Client das optimale Bildformat verwendet werden: WebP für Chrome, JPEG XR für Edge und JPEG für den Rest.
  2. Um die beste visuelle Qualität zu erzielen, muss sie mehrere Varianten jedes Bildes mit verschiedenen Auflösungen generieren: 1x, 1,5x, 2x, 2,5x, 3x und dazwischen sogar ein paar mehr.
  3. Um unnötige Pixel zu vermeiden, muss sie verstehen, was „50% des Darstellungsbereichs des Nutzers tatsächlich bedeutet“ – es gibt viele verschiedene Breiten des Darstellungsbereichs.
  4. Idealerweise möchte sie auch eine resiliente Umgebung bieten, bei der Nutzer in langsameren Netzwerken automatisch eine niedrigere Auflösung abrufen. Schließlich geht es ans Glasklare Zeit.
  5. Die Anwendung bietet auch einige Nutzersteuerelemente, die beeinflussen, welche Bildressource abgerufen werden soll. Diese müssen also ebenfalls berücksichtigt werden.

Dann wird dem Designer klar, dass er ein anderes Bild mit einer Breite von 100% anzeigen muss, wenn der Darstellungsbereich klein ist, um die Lesbarkeit zu optimieren. Das bedeutet, dass wir denselben Vorgang jetzt für ein weiteres Asset wiederholen müssen und dann den Abruf an die Größe des Darstellungsbereichs gebunden sind. Habe ich erwähnt, dass dies schwierig ist? Fangen wir also an. Das picture-Element bringt uns ziemlich weit:

<picture>
    <!-- serve WebP to Chrome and Opera -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w,
        /image/thing-800.webp 800w, /image/thing-1200.webp 1200w,
        /image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w"
    type="image/webp">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w,
        /image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w,
        /image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w"
    type="image/webp">
    <!-- serve JPEGXR to Edge -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w,
        /image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w,
        /image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr 400w,
        /image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr 1200w,
        /image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
    <!-- serve JPEG to others -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
        /image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
        /image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w,
        /image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w,
        /image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w">
    <!-- fallback for browsers that don't support picture -->
    <img src="/image/thing.jpg" width="50%">
</picture>

Wir haben Art Direction und Formatauswahl berücksichtigt und sechs Varianten jedes Bildes bereitgestellt, um Schwankungen in der DVR und der Breite des Darstellungsbereichs auf dem Gerät des Kunden zu berücksichtigen. Beeindruckend!

Leider können wir mit dem Element picture keine Regeln dafür definieren, wie es sich basierend auf dem Verbindungstyp oder der Geschwindigkeit des Clients verhalten sollte. Dennoch kann der User-Agent mithilfe seines Verarbeitungsalgorithmus in einigen Fällen anpassen, welche Ressource abgerufen wird (siehe Schritt 5). Wir müssen nur hoffen, dass der User-Agent schlau genug ist. (Hinweis: Das ist keine der aktuellen Implementierungen.) Ebenso gibt es keine Hooks im Element picture, um anwendungsspezifische Logik zu ermöglichen, die App- oder Nutzereinstellungen berücksichtigt. Um die letzten beiden Bits zu erhalten, müssten wir die gesamte obige Logik in JavaScript verschieben, aber dadurch verzichtet dies auf die Preload Scanner-Optimierungen von picture. Hmm.

Abgesehen von diesen Einschränkungen funktioniert es. Zumindest für dieses Asset. Die eigentliche und langfristige Herausforderung besteht darin, dass wir nicht davon ausgehen können, dass Designer oder Entwickler Code wie diesen für jedes einzelne Asset manuell erstellen müssen. Es ist beim ersten Versuch ein lustiges Denkpuzzle, aber es verliert sofort seine Attraktivität. Wir brauchen Automatisierung. Vielleicht können uns die IDEs oder andere Tools zur Inhaltstransformation ersparen und automatisch den obigen Boilerplate-Code generieren.

Ressourcenauswahl mit Clienthinweisen automatisieren

Atmen Sie tief durch, glauben Sie nicht und sehen Sie sich nun das folgende Beispiel an:

<meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width">
...
<picture>
    <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing">
    <img sizes="100vw" src="/image/thing-crop">
</picture>

Ob du es glaubst oder nicht: Das obige Beispiel reicht aus, um dieselben Funktionen wie das viel längere Bild-Markup oben zu bieten. Außerdem ermöglicht es dem Entwickler, wie wir sehen werden, die volle Kontrolle darüber, wie, welche und wann die Bildressourcen abgerufen werden. Die Magie befindet sich in der ersten Zeile, die die Berichterstellung für Clienthinweise aktiviert und den Browser anweist, das Gerätepixelverhältnis (DPR), die Breite des Layout-Darstellungsbereichs (Viewport-Width) und die beabsichtigte Anzeigebreite (Width) der Ressourcen für den Server anzubieten.

Wenn Clienthinweise aktiviert sind, behält das resultierende clientseitige Markup nur die Darstellungsanforderungen bei. Der Designer muss sich keine Gedanken um Bildtypen, Clientauflösungen, optimale Haltepunkte zur Reduzierung der gelieferten Byte oder andere Kriterien für die Ressourcenauswahl machen. Seien wir ehrlich, sie haben es nie getan und sollte es nicht müssen. Besser ist, dass der Entwickler auch das obige Markup nicht neu schreiben oder erweitern muss, da die tatsächliche Ressourcenauswahl von Client und Server ausgehandelt wird.

Chrome 46 bietet native Unterstützung für die Hinweise DPR, Width und Viewport-Width. Die Hinweise sind standardmäßig deaktiviert. Das <meta http-equiv="Accept-CH" content="..."> oben dient als Opt-in-Signal, das Chrome anweist, die angegebenen Header an ausgehende Anfragen anzuhängen. Sehen wir uns nun die Anfrage- und Antwortheader für eine Beispielbildanfrage an:

Diagramm zur Verhandlung von Clienthinweisen

Chrome bewirbt seine Unterstützung für das WebP-Format über den Accept-Request-Header. Der neue Edge-Browser bietet ebenfalls die Unterstützung für JPEG XR über den Accept-Header an.

Die nächsten drei Anfrageheader sind die Client-Hinweis-Header, die das Gerätepixelverhältnis des Clientgeräts (3x), die Breite des Layout-Darstellungsbereichs (460 px) und die vorgesehene Anzeigebreite der Ressource (230 px) angeben. Dadurch werden dem Server alle erforderlichen Informationen zur Auswahl der optimalen Bildvariante auf der Grundlage eigener Richtlinien bereitgestellt: Verfügbarkeit vorgefertigter Ressourcen, Kosten für die Neucodierung oder Größenanpassung einer Ressource, Beliebtheit einer Ressource, aktuelle Serverlast usw. In diesem speziellen Fall verwendet der Server die Hinweise DPR und Width und gibt eine WebP-Ressource zurück, wie durch die Header Content-Type, Content-DPR und Vary angegeben.

Hier gibt es keine Magie. Wir haben die Ressourcenauswahl vom HTML-Markup in die Anfrage-Antwort-Verhandlung zwischen Client und Server verschoben. Daher betrifft HTML nur die Präsentationsanforderungen und wir können uns darauf verlassen, dass alle Designer und Entwickler schreiben. Die Suche im Bereich zur Bildoptimierung wird hingegen auf Computer übertragen und lässt sich nun problemlos im großen Maßstab automatisieren. Erinnern Sie sich an unsere leistungsbewusste Entwicklerin? Ihre Aufgabe besteht nun darin, einen Bilddienst zu schreiben, der die bereitgestellten Hinweise nutzen und die entsprechende Antwort zurückgeben kann. Sie kann eine beliebige Sprache oder einen beliebigen Server verwenden oder dies von einem Drittanbieterdienst oder einem CDN erledigen lassen.

<img src="/image/thing" sizes="50vw"
        alt="image thing displayed at 50% of viewport width">

Erinnern Sie sich noch an den Typ von oben? Mit Clienthinweisen kann das bescheidene Bild-Tag jetzt auch für DVR, Darstellungsbereich und Breite genutzt werden, ohne dass zusätzliches Markup erforderlich ist. Wenn Sie Art-direction hinzufügen müssen, können Sie das picture-Tag verwenden, wie oben dargestellt. Ansonsten sind all Ihre vorhandenen Bild-Tags wesentlich intelligenter geworden. Mit Clienthinweisen werden vorhandene img- und picture-Elemente optimiert.

Steuerung der Ressourcenauswahl mit Service Worker

ServiceWorker ist im Grunde ein clientseitiger Proxy, der in Ihrem Browser ausgeführt wird. Sie fängt alle ausgehenden Anfragen ab und ermöglicht Ihnen, Antworten zu prüfen, neu zu schreiben, im Cache zu speichern und sogar zu synthetisieren. Images sind nicht anders. Wenn Clienthinweise aktiviert sind, kann der aktive ServiceWorker die Image-Anfragen identifizieren, die bereitgestellten Clienthinweise prüfen und seine eigene Verarbeitungslogik definieren.

self.onfetch = function(event) {
    var req = event.request.clone();
    console.log("SW received request for: " + req.url)
    for (var entry of req.headers.entries()) {
    console.log("\t" + entry[0] +": " + entry[1])
    }
    ...
}
ServiceWorker-Hinweise für Client-Hinweise.

ServiceWorker gibt Ihnen die vollständige clientseitige Kontrolle über die Ressourcenauswahl. Das ist entscheidend. Die Möglichkeiten sind nahezu unendlich:

  • Du kannst die vom User-Agent festgelegten Client-Hinweis-Header-Werte neu schreiben.
  • Sie können neue Werte für Client Hint-Headers an die Anfrage anhängen.
  • Sie können die URL neu schreiben und die Bildanfrage an einen alternativen Server (z.B. CDN) richten.
    • Sie können die Hinweiswerte sogar aus Headern in die URL selbst verschieben, wenn dies die Bereitstellung in Ihrer Infrastruktur erleichtert.
  • Sie können Antworten im Cache speichern und eine eigene Logik für die Bereitstellung von Ressourcen definieren.
  • Sie können Ihre Antwort an die Verbindung der Nutzer anpassen.
  • Sie können Überschreibungen von Anwendungs- und Nutzereinstellungen berücksichtigen.
  • Du kannst wirklich alles tun, was dein Herz begehrt.

Das picture-Element stellt die erforderliche Art-Richtung-Steuerung im HTML-Markup bereit. Clienthinweise liefern Anmerkungen zu resultierenden Bildanfragen, die die Automatisierung der Ressourcenauswahl ermöglichen. ServiceWorker bietet Funktionen zur Anfrage- und Antwortverwaltung auf dem Client. Das ist das Expandable Web in Aktion.

Häufig gestellte Fragen zu Clienthinweisen

  1. Wo sind Clienthinweise verfügbar? Versand in Chrome 46. Unter Überlegungen in Firefox und Edge.

  2. Warum werden Clienthinweise aktiviert? Wir möchten den Aufwand für Websites, die keine Clienthinweise verwenden, minimieren. Um Clienthinweise zu aktivieren, sollte die Website den Accept-CH-Header oder die entsprechende <meta http-equiv>-Anweisung im Seiten-Markup enthalten. Wenn eines dieser Elemente vorhanden ist, hängt der User-Agent die entsprechenden Hinweise an alle Anfragen von Unterressourcen an. Zukünftig werden wir möglicherweise einen zusätzlichen Mechanismus bereitstellen, um diese Einstellung für einen bestimmten Ursprung beizubehalten, sodass bei Navigationsanfragen dieselben Hinweise gesendet werden können.

  3. Warum sind Clienthinweise erforderlich, wenn ServiceWorker vorhanden ist? ServiceWorker hat keinen Zugriff auf Informationen zur Breite von Layout, Ressourcen und Darstellungsbereich. Zumindest nicht ohne kostspielige Roundtrips und eine erhebliche Verzögerung der Bildanfrage – z.B. wenn eine Bildanfrage vom Preload-Parser initiiert wird. Clienthinweise werden in den Browser integriert, um diese Daten als Teil der Anfrage verfügbar zu machen.

  4. Gelten Clienthinweise nur für Bildressourcen? Der Hauptanwendungsfall hinter den Hinweisen für die effektive Darstellung, Breite und Breite des Darstellungsbereichs besteht darin, die Ressourcenauswahl für Bild-Assets zu ermöglichen. Für alle Unterressourcen werden unabhängig vom Typ jedoch dieselben Hinweise bereitgestellt. So erhalten z. B. CSS- und JavaScript-Anfragen dieselben Informationen und können auch zur Optimierung dieser Ressourcen verwendet werden.

  5. Bei einigen Bildanfragen wird die Breite nicht gemeldet. Warum? Der Browser kennt die beabsichtigte Anzeigebreite möglicherweise nicht, weil die Website von der eigentlichen Bildgröße abhängt. Infolgedessen wird der Hinweis für die Breite bei solchen Anfragen sowie bei Anfragen ohne "Anzeigebreite", z.B. bei einer JavaScript-Ressource, weggelassen. Wenn Sie Hinweise für die Breite erhalten möchten, müssen Sie für Ihre Bilder einen Größenwert angeben.

  6. Was ist mit <insert my Favorite hint>? Mit ServiceWorker können Entwickler alle ausgehenden Anfragen abfangen und ändern (z.B. neue Header hinzufügen). Beispielsweise lassen sich auf einfache Weise NetInfo-basierte Informationen hinzufügen, um den aktuellen Verbindungstyp anzugeben. Weitere Informationen finden Sie unter Funktionsberichte mit ServiceWorker. Die in Chrome verfügbaren nativen Hinweise (DPR, Breite, Ressourcenbreite) werden im Browser implementiert, da eine reine Softwarebasierte Implementierung alle Bildanfragen verzögern würde.

  7. Wo finde ich weitere Informationen und Demos und wo finde ich weitere Informationen? Sehen Sie sich die Erklärung an. Wenn Sie Feedback oder andere Fragen haben, können Sie auf GitHub ein Problem melden.