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 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)
- 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 efficace e sostenibile per le immagini e altre risorse con proprietà simili è semplice: 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 degli sviluppatori attenti alle prestazioni
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 creazione.
- 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 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. 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 attente alle prestazioni del team è rimasto per una lunga notte:
- 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.
- 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.
- Per evitare di pubblicare pixel non necessari, deve capire cosa significa "50% dell'area visibile dell'utente": esistono molte diverse larghezze dell'area visibile.
- Idealmente, vuole anche offrire un'esperienza resiliente in cui gli utenti su reti più lente recupereranno automaticamente una risoluzione inferiore. Dopotutto, è tutto ora di usare il vetro.
- 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 poi rendere il recupero condizionale alla dimensione 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, 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 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.
Automazione della selezione delle risorse con i client hint
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 i client hint attivati, il markup lato client risultante mantiene 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 offre supporto nativo per i suggerimenti
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:
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 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 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.
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 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])
}
...
}
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 risposta in base alla connettività degli utenti.
- Puoi utilizzare l'API NetInfo per eseguire query e pubblicizzare le tue preferenze sul server.
- Puoi restituire una risposta alternativa se il recupero è lento.
- Puoi tenere conto delle sostituzioni delle preferenze relative alle applicazioni e agli utenti.
- 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 estendibile in azione.
Domande frequenti sui suggerimenti client
Dove sono disponibili gli indicatori per i clienti? Inviata in Chrome 46. In fase di esame in Firefox e Edge.
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. 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 da poter inviare gli stessi suggerimenti alle richieste di navigazione.Perché abbiamo bisogno di suggerimenti per i client se abbiamo ServiceWorker? ServiceWorker non ha accesso alle informazioni su layout, risorse e larghezza della visualizzazione. 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. Gli indicatori client si integrano con il browser per rendere disponibili questi dati nell'ambito della richiesta.
I client hint sono disponibili solo per le risorse immagini? Il caso d'uso principale alla base degli indicatori DPR, larghezza viewport e larghezza è abilitare 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.
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.
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.
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.