Automazione della selezione delle risorse con hint del client

Ilya Grigorik
Ilya Grigorik

La creazione di contenuti per il web ti offre una copertura impareggiabile. La tua applicazione web è a un solo click di distanza ed è disponibile su quasi tutti i dispositivi connessi, smartphone, tablet, laptop e computer, TV e altro ancora, indipendentemente dal brand o dalla piattaforma. Per offrire un'esperienza ottimale, hai creato un sito adattabile che adatta la presentazione e la funzionalità a ogni fattore di forma e ora stai esaminando l'elenco di controllo del rendimento per assicurarti che l'applicazione venga caricata il più rapidamente possibile: hai ottimizzato il percorso di rendering critico, hai compresso e memorizzato nella cache le risorse di testo e ora stai esaminando le risorse di immagini, che spesso rappresentano la maggior parte dei byte trasferiti. Il problema è che l'ottimizzazione delle immagini è difficile:

  • Determina il formato appropriato (vettore o raster)
  • Determina i formati di codifica ottimali (jpeg, webp e così via)
  • Determina le impostazioni di compressione giuste (con perdita di dati o senza perdita di dati)
  • Determina quali metadati devono essere conservati o rimossi
  • Crea più varianti di ciascuna per ogni display + risoluzione DPR
  • Tenere conto del tipo di rete, della velocità e delle preferenze dell'utente

Si tratta di problemi ben noti. Insieme, creano un ampio spazio di ottimizzazione che noi (gli sviluppatori) spesso trascuriamo o neghiamo. Gli esseri umani non sono molto bravi a esplorare ripetutamente lo stesso spazio di ricerca, soprattutto quando sono coinvolti molti passaggi. I computer, invece, eccellono in questi tipi di attività.

La risposta a una strategia di ottimizzazione buona e sostenibile per le immagini e altre risorse con proprietà simili è semplice: l'automazione. Se stai ottimizzando manualmente le risorse, stai sbagliando: le dimenticherai, ti farai pigrizia o qualcun altro farà questi errori per te, garantito.

La saga dello sviluppatore attento al rendimento

La ricerca nello spazio di ottimizzazione delle immagini ha due fasi distinte: compilazione e tempo di esecuzione.

  • Alcune ottimizzazioni sono intrinseche alla risorsa stessa, ad esempio la selezione del formato e del tipo di codifica appropriati, la regolazione delle impostazioni di compressione per ogni codificatore, l'eliminazione dei metadati non necessari e così via. Questi passaggi possono essere eseguiti in fase di "build".
  • Altre ottimizzazioni sono determinate dal tipo e dalle proprietà del client che le richiede e devono essere eseguite in "runtime": selezione della risorsa appropriata per il DPR e la larghezza di visualizzazione prevista del client, velocità della rete del client, preferenze dell'utente e dell'applicazione e così via.

Gli strumenti di compilazione esistono, ma potrebbero essere migliorati. Ad esempio, è possibile risparmiare molto ottimizzando dinamicamente l'impostazione"qualità" per ogni immagine e per ogni formato, ma non ho ancora visto nessuno utilizzarla al di fuori della ricerca. Si tratta di un'area matura per l'innovazione, ma per gli scopi di questo post mi limiterò a questo. Concentriamoci sulla parte di esecuzione della storia.

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

L'intent dell'applicazione è molto semplice: recuperare e visualizzare l'immagine al 50% del viewport dell'utente. È qui che la maggior parte dei designer si lava le mani e si dirige verso il bar. Nel frattempo, lo sviluppatore attento al rendimento del team è in fortezza per una lunga notte:

  1. Per ottenere la migliore compressione, vuole utilizzare il formato immagine ottimale per ogni cliente: WebP per Chrome, JPEG XR per Edge e JPEG per gli altri.
  2. Per ottenere la migliore qualità visiva, deve generare più varianti di ogni immagine a risoluzioni diverse: 1x, 1,5x, 2x, 2,5x, 3x e forse anche qualche altra intermedia.
  3. Per evitare di pubblicare pixel non necessari, deve capire cosa significa "50% dell'area visibile dell'utente": esistono molte diverse larghezze dell'area visibile.
  4. Idealmente, vuole anche offrire un'esperienza resiliente in cui gli utenti su reti più lente recupereranno automaticamente una risoluzione inferiore. Dopotutto, è il momento di passare al vetro.
  5. L'applicazione espone anche alcuni controlli utente che influiscono sulla risorsa dell'immagine da recuperare, quindi devi tenere conto anche di questo aspetto.

Inoltre, la designer si rende conto che deve visualizzare un'immagine diversa con larghezza al 100% se le dimensioni del viewport sono ridotte per ottimizzare la leggibilità. Ciò significa che ora dobbiamo ripetere la stessa procedura per un altro asset e poi rendere il recupero condizionale alle dimensioni dell'area visibile. Ti ho detto che è difficile? Bene, ok, iniziamo. L'elemento picture ci aiuta molto:

<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>

Abbiamo gestito l'art direction, la selezione del formato e fornito sei varianti di ogni immagine per tenere conto della variabilità del DPR e della larghezza della visualizzazione del dispositivo del cliente. Notevole.

Purtroppo, l'elemento picture non ci consente di definire regole per il suo comportamento in base al tipo o alla velocità di connessione del client. Detto questo, in alcuni casi il suo algoritmo di elaborazione consente all'agente utente di modificare la risorsa recuperata (vedi passaggio 5). Dovremo solo sperare che l'agente utente sia abbastanza intelligente. (nota: nessuna delle implementazioni attuali lo è). Analogamente, non sono presenti hook nell'elemento picture per consentire una logica specifica per l'app che tenga conto delle preferenze dell'app o dell'utente. Per ottenere questi ultimi due bit, dovremmo spostare tutta la logica di cui sopra in JavaScript, ma in questo modo perderemmo le ottimizzazioni dello scanner di precaricamento offerte da picture. Mmh.

A parte queste limitazioni, funziona. Almeno per questo asset specifico. La vera sfida, a lungo termine, è che non possiamo aspettarci che il designer o lo sviluppatore creino manualmente codice come questo per ogni asset. È un rompicapo divertente al primo tentativo, ma perde il suo fascino subito dopo. Abbiamo bisogno di automazione. Forse gli strumenti di trasformazione dei contenuti dell'IDE o di altri possono aiutarci e generare automaticamente il boilerplate riportato sopra.

Automatizzare la selezione delle risorse con gli indizi del client

Fai un bel respiro, metti da parte il tuo scetticismo e considera l'esempio seguente:

<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>

Che tu ci creda o no, l'esempio riportato sopra è sufficiente per offrire tutte le stesse funzionalità del markup delle immagini molto più lungo riportato sopra, oltre a consentire, come vedremo, un controllo completo dello sviluppatore su come, quali e quando vengono recuperate le risorse immagine. La "magia" si trova nella prima riga che attiva i report sui suggerimenti del cliente e indica al browser di pubblicizzare al server il rapporto pixel del dispositivo (DPR), la larghezza del viewport del layout (Viewport-Width) e la larghezza di visualizzazione prevista (Width) delle risorse.

Con gli indicatori client abilitati, il markup lato client risultante conserva solo i requisiti di presentazione. Il designer non deve preoccuparsi di tipi di immagini, risoluzioni del client, breakpoint ottimali per ridurre i byte inviati o altri criteri di selezione delle risorse. Ammettiamolo, non l'hanno mai fatto e non dovrebbero. Inoltre, lo sviluppatore non deve nemmeno riscrivere e ampliare il markup riportato sopra perché la selezione effettiva delle risorse viene negoziata dal client e dal server.

Chrome 46 fornisce il supporto nativo per i prompt DPR, Width e Viewport-Width. I suggerimenti sono disattivati per impostazione predefinita e il codice <meta http-equiv="Accept-CH" content="..."> in alto funge da indicatore di attivazione che indica a Chrome di aggiungere le intestazioni specificate alle richieste in uscita. Dopo aver impostato tutto, esaminiamo le intestazioni di richiesta e risposta per una richiesta di immagine di esempio:

Diagramma della negoziazione dei suggerimenti client

Chrome pubblicizza il supporto del formato WebP tramite l'intestazione della richiesta Accept; allo stesso modo, il nuovo browser Edge pubblicizza il supporto di JPEG XR tramite l'intestazione Accept.

Le tre intestazioni di richiesta successive sono le intestazioni client-hint che pubblicizzano il rapporto pixel del dispositivo del cliente (3x), la larghezza dell'area visibile del layout (460 px) e la larghezza di visualizzazione prevista della risorsa (230 px). In questo modo, il server dispone di tutte le informazioni necessarie per selezionare la variante dell'immagine ottimale in base al proprio insieme di criteri: disponibilità di risorse pregenerate, costo della ricodifica o del ridimensionamento di una risorsa, popolarità di una risorsa, carico corrente del server e così via. In questo caso specifico, il server utilizza gli indicatori DPR e Width e restituisce una risorsa WebP, come indicato dalle intestazioni Content-Type, Content-DPR e Vary.

Non c'è magia qui. Abbiamo spostato la selezione delle risorse dal markup HTML alla negoziazione richiesta-risposta tra il client e il server. Di conseguenza, il codice HTML si occupa solo dei requisiti di presentazione ed è qualcosa che possiamo chiedere a qualsiasi designer e sviluppatore di scrivere, mentre la ricerca nello spazio di ottimizzazione delle immagini viene rimandata ai computer ed è ora facilmente automatizzata su larga scala. Ricordi il nostro sviluppatore attento al rendimento? Ora il suo compito è scrivere un servizio di immagini che possa sfruttare i suggerimenti forniti e restituire la risposta appropriata: può utilizzare qualsiasi linguaggio o server a suo piacimento oppure lasciare che un servizio di terze parti o una CDN lo faccia per suo conto.

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

Inoltre, ricordi questo ragazzo qui sopra? Con i suggerimenti client, il semplice tag immagine ora tiene conto della DPR, della visualizzazione e della larghezza senza alcun markup aggiuntivo. Se devi aggiungere l'indicazione di direzione artistica, puoi utilizzare il tag picture, come illustrato sopra, altrimenti tutti i tag immagine esistenti sono diventati molto più intelligenti. Gli indicatori client migliorano gli elementi img e picture esistenti.

Assumere il controllo della selezione delle risorse con il service worker

ServiceWorker è, in effetti, un proxy lato client in esecuzione nel browser. Intercetta tutte le richieste in uscita e ti consente di ispezionare, riscrivire, memorizzare nella cache e persino sintetizzare le risposte. Le immagini non sono diverse e, con i suggerimenti client attivati, il ServiceWorker attivo può identificare le richieste di immagini, ispezionare i suggerimenti client forniti e definire la propria logica di elaborazione.

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])
    }
    ...
}
Suggerimenti client per serviceWorker.

ServiceWorker ti offre il pieno controllo lato client sulla selezione delle risorse. Questo aspetto è fondamentale. Assorbi bene queste informazioni, perché le possibilità sono quasi infinite:

  • Puoi riscrivere i valori dell'intestazione degli indicatori client impostati dallo user agent.
  • Puoi aggiungere alla richiesta nuovi valori degli intestazioni degli indicatori client.
  • Puoi riscrivere l'URL e indirizzare la richiesta di immagine a un server alternativo (ad es. CDN).
    • Puoi persino spostare i valori di suggerimento dalle intestazioni all'URL stesso, se questo semplifica il deployment nell'infrastruttura.
  • Puoi memorizzare nella cache le risposte e definire la tua logica per le risorse da pubblicare.
  • Puoi adattare la tua risposta in base alla connettività degli utenti.
  • Puoi tenere conto delle sostituzioni delle preferenze dell'applicazione e dell'utente.
  • Puoi fare tutto ciò che vuoi.

L'elemento picture fornisce il controllo necessario dell'art direction nel markup HTML. Gli indicatori client forniscono annotazioni sulle richieste di immagini risultanti che consentono l'automazione della selezione delle risorse. ServiceWorker fornisce funzionalità di gestione delle richieste e delle risposte sul client. Questo è il web espandibile in azione.

Domande frequenti sui suggerimenti client

  1. Dove sono disponibili gli indicatori per i clienti? Inviata in Chrome 46. In fase di esame in Firefox e Edge.

  2. Perché è necessario attivare i suggerimenti per i clienti? Vogliamo ridurre al minimo il sovraccarico per i siti che non utilizzeranno i suggerimenti per i client. Per attivare i suggerimenti per i client, il sito deve fornire l'intestazione Accept-CH o l'istruzione <meta http-equiv> equivalente nel markup della pagina. Se è presente uno di questi, lo user agent aggiungerà i suggerimenti appropriati a tutte le richieste di risorse secondarie. In futuro, potremmo fornire un ulteriore meccanismo per mantenere questa preferenza per una determinata origine, in modo da poter inviare gli stessi suggerimenti alle richieste di navigazione.

  3. Perché abbiamo bisogno di suggerimenti per i client se abbiamo ServiceWorker? ServiceWorker non ha accesso alle informazioni su layout, risorse e larghezza della visualizzazione. Almeno non senza introdurre costosi round trip e ritardare notevolmente la richiesta di immagini, ad esempio quando una richiesta di immagini viene avviata dal parser di precaricamento. Gli indicatori client si integrano con il browser per rendere disponibili questi dati nell'ambito della richiesta.

  4. I suggerimenti per i client sono solo per le risorse immagine? Il caso d'uso principale alla base degli indicatori DPR, larghezza viewport e larghezza è abilitare la selezione delle risorse per gli asset immagine. Tuttavia, gli stessi suggerimenti vengono forniti per tutte le risorse secondarie, indipendentemente dal tipo. Ad esempio, anche le richieste CSS e JavaScript ricevono le stesse informazioni e possono essere utilizzate per ottimizzare anche queste risorse.

  5. Perché alcune richieste di immagini non riportano la larghezza? Il browser potrebbe non conoscere la larghezza di visualizzazione prevista perché il sito si basa sulle dimensioni intrinseche dell'immagine. Di conseguenza, il suggerimento Larghezza viene omesso per queste richieste e per quelle che non hanno "larghezza del display", ad esempio una risorsa JavaScript. Per ricevere suggerimenti relativi alla larghezza, assicurati di specificare un valore sizes sulle tue immagini.

  6. Che ne dici di <insert my favorite hint>? ServiceWorker consente agli sviluppatori di intercettare e modificare (ad es. aggiungere nuovi parametri) tutte le richieste in uscita. Ad esempio, è facile aggiungere informazioni basate su NetInfo per indicare il tipo di connessione corrente. Consulta "Report sulle funzionalità con ServiceWorker". Gli indicatori "nativizzati" forniti in Chrome (DPR, Width, Resource-Width) sono implementati nel browser perché un'implementazione puramente basata su software ritarderebbe tutte le richieste di immagini.

  7. Dove posso trovare ulteriori informazioni, guardare altre demo e altro ancora? Consulta il documento esplicativo e non esitare a aprire un problema su GitHub se hai feedback o altre domande.