Automazione della selezione delle risorse con hint del client

Ilya Grigorik
Ilya Grigorik

Creare per il Web ti offre una copertura senza precedenti. Basta un clic per creare la tua applicazione web ed è disponibile su quasi tutti i dispositivi connessi: smartphone, tablet, laptop e computer, TV e molto altro, indipendentemente dal brand o dalla piattaforma. Per offrire la migliore esperienza possibile, hai creato un sito adattabile che adatta la presentazione e la funzionalità a ogni fattore di forma e ora stai eseguendo l'elenco di controllo delle prestazioni per garantire che l'applicazione si carichi 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 immagine, che spesso vengono trasferite per la maggior parte dell'account. Il problema è che l'ottimizzazione delle immagini è difficile:

  • Determinare il formato appropriato (vettore e raster)
  • Determinare i formati di codifica ottimali (jpeg, webp e così via).
  • Determinare le impostazioni di compressione corrette (perdita o senza perdita)
  • Determina quali metadati devono essere conservati o rimossi
  • Realizzare più varianti di ciascun display + risoluzione DPR
  • ...
  • Considera il tipo di rete, la velocità e le preferenze dell'utente

Individualmente, si tratta di problemi ben compresi. Collettivamente, creano un ampio spazio di ottimizzazione che noi (gli sviluppatori) spesso trascuriamo o trascuriamo. Gli esseri umani non esplorano lo stesso spazio di ricerca ripetutamente, soprattutto quando sono necessari molti passaggi. I computer, invece, eccellono in questo tipo di attività.

La risposta a una strategia di ottimizzazione efficace e sostenibile per le immagini e altre risorse con proprietà simili è semplice: automazione. Se ottimizzi le risorse manualmente, è sbagliato: ti dimenticherai, diventerai pigro o qualcun altro farà questi errori per te, garantito.

La saga degli sviluppatori attenti alle prestazioni

La ricerca nell'area di ottimizzazione delle immagini prevede due fasi distinte: tempo di creazione e tempo di esecuzione.

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

Gli strumenti per la fase di creazione esistono, ma potrebbero essere migliorati. Ad esempio, è possibile risparmiare moltissimo regolando dinamicamente l'impostazione della "qualità" per ogni immagine e ciascun formato dell'immagine, ma non vedo ancora che qualcuno l'abbia effettivamente al di fuori della ricerca. È un'area pronta per l'innovazione, ma ai fini di questo post la lascio lì. Concentriamoci sulla parte della storia relativa al tempo di esecuzione.

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

L'intent dell'applicazione è molto semplice: recupera e visualizza l'immagine nel 50% dell'area visibile dell'utente. È qui che quasi tutti i designer si lavano le mani e la testa per il bar. Nel frattempo, lo sviluppatore attente alle prestazioni del team è rimasto per una lunga notte:

  1. Per ottenere la compressione ottimale, vuole utilizzare il formato dell'immagine ottimale per ogni client: WebP per Chrome, JPEG XR per Edge e JPEG al resto.
  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 variante intermedia.
  3. Per evitare di fornire pixel non necessari, deve capire cosa significa effettivamente il "50% dell'area visibile dell'utente": esistono molte larghezze dell'area visibile diverse.
  4. Idealmente, vuole anche offrire un'esperienza resiliente in cui gli utenti che utilizzano reti più lente recupereranno automaticamente una risoluzione più bassa. Dopotutto, è tutto ora di usare il vetro.
  5. L'applicazione espone anche alcuni controlli utente che influiscono sulla risorsa immagine da recuperare, quindi è necessario tenere conto anche di questo.

A quel punto il designer si rende conto che, per ottimizzare la leggibilità, deve mostrare un'immagine diversa con larghezza al 100% se l'area visibile è di dimensioni ridotte. Ciò significa che ora dobbiamo ripetere la stessa procedura per un altro asset e quindi rendere il recupero condizionale alla dimensione dell'area visibile. Ho detto che queste cose sono difficili? Bene, ok, iniziamo. L'elemento picture ci porterà abbastanza lontano:

<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 la direzione artistica e la selezione del formato e abbiamo fornito sei varianti di ciascuna immagine per tenere conto della variabilità della DPR e della larghezza dell'area visibile del dispositivo del cliente. Notevole.

Purtroppo l'elemento picture non ci consente di definire regole relative al suo comportamento in base al tipo di connessione o alla velocità del client. Detto questo, in alcuni casi il suo algoritmo di elaborazione consente allo user agent di modificare la risorsa che recupera (vedi il passaggio 5). Dobbiamo solo sperare che lo user agent sia abbastanza intelligente. Nota: nessuna delle implementazioni attuali lo è. Analogamente, l'elemento picture non presenta hook per consentire la logica specifica dell'app che tiene conto delle preferenze dell'app o dell'utente. Per ottenere gli ultimi due bit, dovremmo spostare tutta la logica di cui sopra in JavaScript, ma questo perde le ottimizzazioni dello scanner di precaricamento offerte da picture. Mmh.

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

Automazione della selezione delle risorse con i client hint

Fai un bel respiro, sospendi la tua incredulità e considera il seguente esempio:

<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 le stesse funzionalità del markup delle immagini molto più lungo di cui sopra, inoltre, come vedremo, offre agli sviluppatori il pieno controllo su come, quali e quando vengono recuperate le risorse delle immagini. Il "magico" è nella prima riga che abilita i report sui client hint e indica al browser di pubblicizzare al server le proporzioni pixel del dispositivo (DPR), la larghezza dell'area visibile del layout (Viewport-Width) e la larghezza di visualizzazione prevista (Width) delle risorse.

Con i client hint attivati, il markup lato client risultante mantiene solo i requisiti di presentazione. Il designer non deve preoccuparsi dei tipi di immagini, delle risoluzioni dei client, dei punti di interruzione ottimali per ridurre i byte consegnati o di altri criteri di selezione delle risorse. Ammettiamolo, non l'hanno mai fatto e non dovrebbero esserlo. Inoltre, lo sviluppatore non ha bisogno di riscrivere ed espandere il markup di cui sopra, perché la selezione effettiva delle risorse è negoziata dal client e dal server.

Chrome 46 offre supporto nativo per i suggerimenti DPR, Width e Viewport-Width. I suggerimenti sono disattivati per impostazione predefinita e il <meta http-equiv="Accept-CH" content="..."> riportato sopra funge da indicatore di attivazione che indica a Chrome di aggiungere le intestazioni specificate alle richieste in uscita. Esaminiamo ora le intestazioni della richiesta e della risposta di una richiesta di immagine di esempio:

Diagramma di negoziazione dei client hint

Chrome pubblicizza il supporto del formato WebP tramite l'intestazione Accept request; in modo simile, il nuovo browser Edge pubblicizza il supporto di JPEG XR tramite l'intestazione Accept.

Le tre intestazioni di richiesta successive sono le intestazioni del suggerimento client che pubblicizzano il rapporto pixel del dispositivo del client (3x), la larghezza dell'area visibile del layout (460 px) e la larghezza di visualizzazione prevista della risorsa (230 px). Ciò fornisce al server tutte le informazioni necessarie per selezionare la variante di immagine ottimale in base al suo insieme di criteri: disponibilità di risorse pregenerate, costo della ricodifica o ridimensionamento di una risorsa, popolarità di una risorsa, carico attuale del server e così via. In questo caso particolare, il server utilizza i suggerimenti 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 riguarda solo i requisiti di presentazione ed è qualcosa di cui possiamo fidarci di qualsiasi designer e sviluppatore, mentre la ricerca tramite lo spazio di ottimizzazione delle immagini viene differita ai computer e ora è facilmente automatizzata su larga scala. Ricordi il nostro sviluppatore attento alle prestazioni? Il suo compito ora è scrivere un servizio immagine che possa sfruttare i suggerimenti forniti e che restituisca la risposta appropriata: può utilizzare il linguaggio o il server che preferisce oppure consentire a un servizio di terze parti o a una CDN di farlo per suo conto.

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

E ti ricordi di questo tizio qui sopra? Con i suggerimenti del client, il semplice tag immagine è ora sensibile a DPR, area visibile e larghezza senza alcun markup aggiuntivo. Se devi aggiungere indicazioni artistiche, puoi utilizzare il tag picture, come illustrato sopra, altrimenti tutti i tag immagine esistenti sono diventati molto più intelligenti. I client hint migliorano gli elementi img e picture esistenti.

Assumi il controllo sulla selezione delle risorse con il service worker

ServiceWorker è, a tutti gli effetti, un proxy lato client in esecuzione nel tuo browser. Intercetta tutte le richieste in uscita e consente di ispezionare, riscrivere, memorizzare nella cache e persino sintetizzare le risposte. Le immagini non sono diverse e, con i client hint abilitati, il ServiceWorker attivo può identificare le richieste di immagini, esaminare i client hint forniti e definire la propria logica di elaborazione.

self. {
    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 Client Hint.

ServiceWorker ti offre il controllo lato client sulla selezione delle risorse. Questo aspetto è fondamentale. Lasciamo perdere, perché le possibilità sono quasi infinite:

  • Puoi riscrivere i valori dell'intestazione Client Hint impostati dallo user agent.
  • Puoi aggiungere alla richiesta nuovi valori di intestazioni di client hint.
  • Puoi riscrivere l'URL e indirizzare la richiesta dell'immagine a un server alternativo (ad es. CDN).
    • È anche possibile spostare i valori hint dalle intestazioni e nell'URL stesso se ciò semplifica il deployment nell'infrastruttura.
  • Puoi memorizzare nella cache le risposte e definire la logica per la pubblicazione delle risorse.
  • Puoi adattare la risposta in base alla connettività degli utenti.
  • Puoi tenere conto delle sostituzioni delle preferenze relative alle applicazioni e agli utenti.
  • Puoi... fare tutto ciò che il tuo cuore desidera, sul serio.

L'elemento picture fornisce il controllo art-direct necessario nel markup HTML. I client hint 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 estendibile in azione.

Domande frequenti sui suggerimenti del client

  1. Dove sono disponibili i client hint? Spedito in Chrome 46. In fase di valutazione in Firefox ed Edge.

  2. Perché i suggerimenti del cliente devono essere attivati? Vogliamo ridurre al minimo l'overhead per i siti che non utilizzano i suggerimenti del client. Per attivare i suggerimenti client, il sito deve fornire l'intestazione Accept-CH o un'istruzione <meta http-equiv> equivalente nel markup della pagina. Quando uno di questi elementi è presente, lo user agent aggiungerà i suggerimenti appropriati a tutte le richieste di sottorisorse. In futuro, potremmo fornire un ulteriore meccanismo per mantenere questa preferenza per una determinata origine, in modo che gli stessi suggerimenti vengano forniti nelle richieste di navigazione.

  3. Perché abbiamo bisogno dei suggerimenti del client se abbiamo ServiceWorker? ServiceWorker non ha accesso alle informazioni su layout, risorse e larghezza dell'area visibile. O almeno, non senza introdurre costosi andata e ritorno e ritardando significativamente la richiesta dell'immagine, ad esempio quando una richiesta di immagine viene avviata dall'analizzatore sintattico di precaricamento. I client hint si integrano con il browser per rendere questi dati disponibili come parte della richiesta.

  4. I client hint sono disponibili solo per le risorse di immagini? Il caso d'uso principale alla base dei suggerimenti DPR, Viewport-Larghezza e Larghezza è consentire la selezione delle risorse per gli asset immagine. Tuttavia, vengono forniti gli stessi suggerimenti 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 indicano la larghezza? Il browser potrebbe non conoscere la larghezza di visualizzazione prevista perché il sito si basa sulle dimensioni intrinseche dell'immagine. Di conseguenza, l'hint Larghezza viene omesso per queste richieste e per quelle che non hanno una "larghezza di visualizzazione", ad esempio una risorsa JavaScript. Per ricevere i suggerimenti sulla larghezza, assicurati di specificare un valore dimensioni per le immagini.

  6. Che ne dici di <insert my preferred hint>? ServiceWorker consente agli sviluppatori di intercettare e modificare (ad es. aggiungere nuove intestazioni) tutte le richieste in uscita. Ad esempio, è facile aggiungere informazioni basate su NetInfo per indicare il tipo di connessione corrente. Consulta "Report sulle capacità con ServiceWorker". I suggerimenti "nativi" forniti in Chrome (DPR, Larghezza, Larghezza della risorsa) vengono implementati nel browser perché un'implementazione basata su software puro ritarda tutte le richieste di immagini.

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