Automazione della selezione delle risorse con hint del client

Ilya Grigorik

Creare per il web ti offre una copertura senza pari. La tua applicazione web è a portata di clic ed è disponibile su quasi tutti i dispositivi connessi, come smartphone, tablet, laptop e computer, TV e altro ancora, 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. Ora devi esaminare l'elenco di controllo delle prestazioni 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 immagine, che spesso rappresentano la maggior parte dei byte. Il problema è che l'ottimizzazione delle immagini è difficile:

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

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

La risposta a una strategia di ottimizzazione valida e sostenibile per le immagini e altre risorse con proprietà simili è semplice: l'automazione. Se stai regolando manualmente le tue risorse, commetti un errore: ti dimenticherai, diventerai pigro o qualcun altro commetterà questo errore per te (garantito).

La saga degli sviluppatori attenti al rendimento

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

  • Alcune ottimizzazioni sono intrinseche alla risorsa stessa, ad esempio la selezione del formato e il tipo di codifica appropriati, l'ottimizzazione 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 la richiede e devono essere eseguite in fase di runtime: selezionando la risorsa appropriata per il DPR del cliente e la larghezza di visualizzazione prevista, tenendo conto della velocità di rete del client, delle preferenze dell'utente e dell'applicazione e così via.

Gli strumenti in fase di compilazione esistono, ma potrebbero essere migliorati. Ad esempio, è possibile risparmiare ottimizzando dinamicamente l'impostazione"Qualità" per ogni immagine e ogni formato, ma non ho ancora notato che qualcuno la utilizzi al di fuori della ricerca. Questo è un'area matura per l'innovazione, ma ai fini di questo post la lascio qui. 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: recuperare e visualizzare l'immagine al 50% dell'area visibile dell'utente. È qui che quasi tutti i designer si lavano le mani e si dirigono al bar. Allo stesso tempo, lo sviluppatore del team, attento alle prestazioni, trascorre una lunga notte:

  1. Per ottenere la migliore compressione, vuole utilizzare il formato dell'immagine ottimale per ogni client: WebP per Chrome, JPEG XR per Edge e JPEG per tutti gli altri.
  2. Per ottenere la migliore qualità visiva, deve generare più varianti di ciascuna immagine a diverse risoluzioni: 1x, 1,5x, 2x, 2,5x, 3x e forse anche alcune di più in mezzo.
  3. Per evitare di caricare pixel inutili, deve capire cosa significa davvero "il 50% dell'area visibile dell'utente", in quanto esistono molte larghezze dell'area visibile diverse.
  4. Idealmente, vuole anche offrire un'esperienza resiliente in cui gli utenti sulle reti più lente recuperino automaticamente una risoluzione più bassa. Dopo tutto, è ora di passare al bicchiere.
  5. L'applicazione espone anche alcuni controlli utente che influiscono sulla risorsa immagine da recuperare, quindi c'è anche quello da tenere in considerazione.

Il designer si rende conto che, per ottimizzare la leggibilità, deve mostrare un'immagine diversa con una larghezza del 100%. Ciò significa che ora dobbiamo ripetere la stessa procedura per un altro asset e quindi rendere il recupero condizionale in base alle dimensioni 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 ogni immagine per tenere conto della variabilità della DPR e della larghezza dell'area visibile del dispositivo del cliente. Fantastico!

Purtroppo l'elemento picture non consente di definire alcuna regola su come dovrebbe comportarsi in base al tipo o alla velocità di connessione del client. Detto questo, in alcuni casi il suo algoritmo di elaborazione consente allo user agent di regolare la risorsa recupera (vedi il passaggio 5). Dovremo solo sperare che lo user agent sia abbastanza intelligente. Nota: nessuna delle attuali implementazioni lo è. Analogamente, l'elemento picture non prevede hook per consentire una logica specifica dell'app che tiene conto delle preferenze dell'app o dell'utente. Per ottenere questi ultimi due bit, dovremmo spostare tutta la logica precedente in JavaScript, ma perderanno le ottimizzazioni dello scanner di precaricamento offerte da picture. Mmh.

A parte queste limitazioni, funziona. Almeno per questa specifica risorsa. La vera sfida a lungo termine qui è che non possiamo aspettarci che il designer o lo sviluppatore creino un codice come questo per ogni singolo asset. È un rompicapo divertente al primo tentativo, ma subito dopo perde la sua attrattiva. Ci serve l'automazione. Forse l'IDE o altri strumenti per la trasformazione dei contenuti possono farci risparmiare e generare automaticamente il boilerplate sopra.

Automazione della selezione delle risorse con hint del client

Fai un respiro profondo, sospendi la tua incredulità e ora 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 dell'immagine molto più lungo riportato sopra. Inoltre, come vedremo, consente allo sviluppatore di avere il pieno controllo su come, quali e quando le risorse immagine vengono recuperate. La "magia" si trova nella prima riga che attiva i report sui suggerimenti client e indica al browser di pubblicizzare le proporzioni pixel del dispositivo (DPR), la larghezza dell'area visibile del layout (Viewport-Width) e la larghezza di visualizzazione prevista (Width) delle risorse al server.

Con i suggerimenti del client attivati, il markup lato client risultante conserva 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 pubblicati o di altri criteri di selezione delle risorse. Ammettiamolo, non è mai stato così e non dovrebbe esserlo. Inoltre, lo sviluppatore non ha bisogno di riscrivere ed espandere il markup di cui sopra perché la selezione effettiva delle risorse viene negoziata dal client e dal server.

Chrome 46 fornisce supporto nativo per i suggerimenti DPR, Width e Viewport-Width. I suggerimenti sono disattivati per impostazione predefinita e l'elemento <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. Una volta che questa impostazione è attiva, esaminiamo le intestazioni di richiesta e risposta per un esempio di richiesta di immagine:

Diagramma della negoziazione dei suggerimenti per i clienti

Chrome pubblicizza il supporto per il formato WebP tramite l'intestazione della richiesta Accept; il nuovo browser Edge pubblicizza in modo simile il supporto per JPEG XR tramite l'intestazione Accept.

Le tre intestazioni delle richieste successive sono le intestazioni del suggerimento client che pubblicizzano il rapporto pixel del dispositivo del client (3 volte), 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 ottimale dell'immagine in base al proprio insieme di criteri: disponibilità delle risorse pregenerate, costo della ricodifica o del ridimensionamento di una risorsa, popolarità di una risorsa, carico attuale del server e così via. In questo caso particolare, il server utilizza gli hint 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 nella negoziazione richiesta-risposta tra client e server. Di conseguenza, l'HTML è interessato solo ai requisiti di presentazione ed è qualcosa su cui possiamo fidarci di qualsiasi designer e sviluppatore, mentre la ricerca nello spazio di ottimizzazione delle immagini viene differita ai computer ed è ora facilmente automatizzata su larga scala. Ricordi il nostro sviluppatore attento alle prestazioni? Il suo compito ora è scrivere un servizio di immagini in grado di sfruttare i suggerimenti forniti e restituire la risposta appropriata: può usare il linguaggio o il server che preferisce 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">

Ricordi il tizio sopra? Con i suggerimenti del client, l'umile tag immagine ora è sensibile a DPR, area visibile e larghezza senza alcun markup aggiuntivo. Se devi aggiungere un art-direction, puoi usare il tag picture, come illustrato sopra. In caso contrario, tutti i tag immagine esistenti sono diventati molto più intelligenti. I suggerimenti dei client migliorano gli elementi img e picture esistenti.

Assunzione del controllo sulla 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 consente di ispezionare, riscrivere, memorizzare nella cache e persino sintetizzare le risposte. Le immagini non sono diverse e, con i client hint attivati, il ServiceWorker attivo può identificare le richieste di immagine, esaminare 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])
    }
    ...
}
ServiceWorker hint client.

ServiceWorker ti offre il controllo lato client completo sulla selezione delle risorse. Questo è fondamentale. Le possibilità sono infinite, perché sono quasi infinite.

  • Puoi riscrivere i valori dell'intestazione dei suggerimenti client impostati dallo user agent.
  • Puoi aggiungere alla richiesta nuovi valori per le intestazioni dei suggerimenti client.
  • Puoi riscrivere l'URL e puntare la richiesta dell'immagine a un server alternativo (ad es. CDN).
    • Puoi anche spostare i valori dei suggerimenti dalle intestazioni all'URL stesso, se ciò semplifica il deployment nella tua infrastruttura.
  • Puoi memorizzare le risposte nella cache e definire la logica per la quale pubblicare le risorse.
  • Puoi adattare la tua risposta in base alla connettività degli utenti.
  • Puoi tenere conto delle sostituzioni delle preferenze di utenti e applicazioni.
  • Puoi... fare tutto ciò che il tuo cuore desidera, davvero.

L'elemento picture fornisce il controllo art-direction necessario nel markup HTML. I client hint forniscono annotazioni sulle richieste di immagine 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 client

  1. Dove sono disponibili i suggerimenti per i clienti? Spedito con Chrome 46. In fase di valutazione in Firefox e Edge.

  2. Perché è attiva l'attivazione dei suggerimenti client? Vogliamo ridurre al minimo l'overhead per i siti che non utilizzano i suggerimenti client. Per attivare i suggerimenti del client, il sito deve fornire l'intestazione Accept-CH o l'istruzione <meta http-equiv> equivalente nel markup della pagina. Se uno di questi è 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, il che consentirà di fornire gli stessi suggerimenti per le richieste di navigazione.

  3. Perché abbiamo bisogno dei suggerimenti del client se abbiamo ServiceWorker? ServiceWorker non ha accesso alle informazioni su layout, risorsa e larghezza dell'area visibile. Almeno, non senza introdurre costosi round trip e ritardando notevolmente la richiesta dell'immagine, ad esempio quando una richiesta di immagine viene avviata dall'analizzatore sintattico di precaricamento. I suggerimenti client si integrano con il browser per rendere disponibili questi dati nell'ambito della richiesta.

  4. I suggerimenti del client sono disponibili solo per le risorse immagine? Il caso d'uso principale alla base dei suggerimenti relativi a DPR, larghezza dell'area visibile e larghezza è quello di 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 anche per ottimizzare queste risorse.

  5. Alcune richieste di immagine non indicano la larghezza. Perché? Il browser potrebbe non conoscere la larghezza di visualizzazione prevista perché il sito si basa sulle dimensioni intrinseche dell'immagine. Di conseguenza, il suggerimento relativo alla larghezza viene omesso per queste richieste e per quelle che non hanno "larghezza di visualizzazione", ad esempio una risorsa JavaScript. Per ricevere i suggerimenti sulla larghezza, assicurati di specificare un valore delle dimensioni sulle immagini.

  6. E <inserisci il mio suggerimento preferito>? 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 funzionalità con ServiceWorker". I suggerimenti "nativi" forniti in Chrome (DPR, larghezza, larghezza delle risorse) vengono implementati nel browser perché un'implementazione pura basata su SW ritarda tutte le richieste di immagine.

  7. Dove posso trovare ulteriori informazioni, vedere altre demo e cosa posso fare? Consulta il documento esplicativo e, se hai feedback o altre domande, non esitare ad aprire un problema su GitHub.