Automatizzare la compressione e la codifica

Fai in modo che la generazione di origini di immagini ad alte prestazioni sia parte integrante del tuo processo di sviluppo.

Tutte le sintassi descritte in questo corso, dalla codifica dei dati delle immagini al markup ad alta densità di informazioni alla base delle immagini adattabili, sono metodi che consentono alle macchine di comunicare con le macchine. Hai scoperto vari modi in cui un browser client può comunicare le sue esigenze a un server e un server per rispondere in natura. Il markup delle immagini adattabili (srcset e sizes, in particolare) riesce a descrivere una quantità scioccante di informazioni in relativamente meno caratteri. Nel migliore o peggiore senso, questa brevità è data dalla sua progettazione: rendere queste sintassi meno concise e così più facili da analizzare per gli sviluppatori, avrebbe potuto renderle più difficili da analizzare per un browser. Maggiore è la complessità aggiunta a una stringa, maggiore è il potenziale di errori del parser o di differenze involontarie di comportamento da un browser all'altro.

Una finestra per la codifica automatica delle immagini.

Tuttavia, la stessa caratteristica che può far sembrare questi soggetti così minacciosi può anche fornirti delle soluzioni: una sintassi facilmente leggibile dalle macchine è una sintassi più facilmente scritta dalle macchine. Quasi sicuramente avrete riscontrato molti esempi di codifica e compressione automatiche delle immagini in qualità di utente del web: qualsiasi immagine caricata sul web tramite piattaforme di social media, sistemi di gestione dei contenuti (CMS) e persino client email passano quasi invariabilmente attraverso un sistema che le ridimensiona, le ricodifica e le comprime.

Allo stesso modo, tramite plug-in, librerie esterne, strumenti per i processi di compilazione autonomi o utilizzo responsabile dello scripting lato client, il markup adattabile delle immagini si presta facilmente all'automazione.

Questi sono i due problemi principali relativi all'automazione delle prestazioni delle immagini: gestire la creazione delle immagini (le relative codifiche, la compressione e le fonti alternative che utilizzerai per compilare un attributo srcset) e la generazione del nostro markup rivolto agli utenti. In questo modulo scoprirai alcuni approcci comuni alla gestione delle immagini nell'ambito di un flusso di lavoro moderno, che si tratti di una fase automatizzata del processo di sviluppo, tramite il framework o il sistema di gestione dei contenuti alla base del tuo sito, oppure completamente astratti da una rete CDN (Content Delivery Network) dedicata.

Automatizzare la compressione e la codifica

È improbabile che ti trovi in una posizione in cui puoi dedicare del tempo a determinare manualmente la codifica e il livello di compressione ideali per ogni singola immagine destinata a un progetto; né tu vorresti farlo. Importante in quanto è importante ridurre al minimo le dimensioni di trasferimento delle immagini , perfezionare le impostazioni di compressione e salvare nuovamente le origini alternative per ogni asset immagine destinato a un sito web di produzione introdurrebbe un enorme collo di bottiglia nel tuo lavoro quotidiano.

Come hai appreso durante la lettura dei vari formati e tipi di compressione delle immagini, la codifica più efficiente di un'immagine sarà sempre determinata dai contenuti. Inoltre, come hai appreso nella sezione Immagini adattabili, le dimensioni alternative necessarie per le origini delle immagini saranno dettate dalla posizione occupata dalle immagini nel layout della pagina. In un flusso di lavoro moderno, dovrai affrontare queste decisioni in modo olistico anziché individuale, determinando un insieme di valori predefiniti sensati per le immagini, per adattarle al meglio ai contesti in cui sono destinate a essere utilizzate.

Quando si scelgono le codifiche per una directory di immagini fotografiche, AVIF è il chiaro vincitore in termini di qualità e dimensioni di trasferimento, ma ha un supporto limitato, WebP offre un elemento di riserva ottimizzato e moderno, mentre JPEG è l'impostazione predefinita più affidabile. Le dimensioni alternative che dobbiamo produrre per le immagini che devono occupare una barra laterale in un layout di pagina varieranno molto da quelle destinate a occupare l'intera area visibile del browser nei punti di interruzione più elevati. Le impostazioni di compressione richiederanno un occhio alla sfocatura e alla compressione degli artefatti in più file risultanti, lasciando meno spazio per ritagliare ogni possibile byte da ogni immagine in cambio di un flusso di lavoro più flessibile e affidabile. Per riassumere, seguirai lo stesso processo decisionale che hai imparato in questo corso, scrivendo su larga scala.

Per quanto riguarda l'elaborazione stessa, esistono un numero enorme di librerie di elaborazione di immagini open source che offrono metodi per convertire, modificare e modificare le immagini in batch, concorrendo in termini di velocità, efficienza e affidabilità. Queste librerie di elaborazione ti consentono di applicare impostazioni di codifica e compressione a intere directory di immagini contemporaneamente, senza dover aprire un software di modifica delle immagini e in modo da preservare le origini delle immagini originali nel caso in cui queste impostazioni debbano essere regolate all'istante. Sono concepiti per essere eseguiti in una vasta gamma di contesti, dall'ambiente di sviluppo locale al server web stesso. Ad esempio, ImageMin per Node.js, incentrato sulla compressione, può essere esteso ad applicazioni specifiche tramite una serie di plug-in, mentre ImageMagick

Queste librerie di elaborazione delle immagini consentono agli sviluppatori di creare strumenti dedicati all'ottimizzazione senza soluzione di continuità delle immagini nell'ambito dei processi di sviluppo standard, garantendo che il progetto faccia sempre riferimento a origini di immagini pronte per la produzione con il minimo sovraccarico possibile.

Strumenti e flussi di lavoro di sviluppo locale

Task-runner e bundler come Grunt, Gulp o Webpack possono essere utilizzati per ottimizzare gli asset immagine insieme ad altre attività comuni correlate al rendimento, come la minimizzazione di CSS e JavaScript. Per illustrare il concetto, prendiamo in considerazione un caso d'uso relativamente semplice: una directory nel tuo progetto contiene una decina di immagini fotografiche da utilizzare su un sito web pubblico.

Innanzitutto, dovrai garantire una codifica coerente ed efficiente per queste immagini. Come hai imparato nei moduli precedenti, WebP è una soluzione predefinita efficiente per le immagini fotografiche, sia in termini di qualità che di dimensioni dei file. WebP è ben supportato, ma non universalmente, quindi dovrai includere anche un video di riserva sotto forma di JPEG progressivo. Poi, per utilizzare l'attributo srcset per una pubblicazione efficiente di questi asset, devi produrre più dimensioni alternative per ogni codifica.

Anche se si tratta di un lavoro ripetitivo e dispendioso in termini di tempo, se eseguito con software di editing di immagini, gli strumenti di esecuzione delle attività come Gulp sono progettati per automatizzare esattamente questo tipo di ripetizioni. Il plug-in gulp-responsive, che utilizza Sharp, è una delle tante opzioni che seguono uno schema simile: raccogliere tutti i file in una directory di origine, ricodificarli e comprimerli sulla base della stessa "qualità" standardizzata che hai appreso in Formati delle immagini e compressione. I file risultanti vengono quindi inviati a un percorso definito da te, pronto a essere utilizzato come riferimento negli attributi src degli elementi img rivolti all'utente, lasciando invariati i file originali.

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.webp = function() {
  return src('./src-img/*')
    .pipe(respimg({
      '*': [{
        quality: 70,
        format: ['webp', 'jpeg'],
        progressive: true
      }]
  }))
  .pipe(dest('./img/'));
}

Con un processo come questo in atto, non si verificherebbe alcun danno all'ambiente di produzione se qualcuno del progetto aggiungesse inavvertitamente una fotografia codificata come PNG truecolor di grandi dimensioni alla directory contenente le origini delle immagini originali. Indipendentemente dalla codifica dell'immagine originale, questa attività produrrà un WebP efficiente e un JPEG progressivo affidabile di riserva, e a un livello di compressione che può essere facilmente regolato al volo. Naturalmente, questo processo garantisce anche che i file immagine originali vengano conservati all'interno dell'ambiente di sviluppo del progetto, il che significa che queste impostazioni possono essere modificate in qualsiasi momento con solo l'output automatico sovrascritto.

Per generare più file, devi trasferire più oggetti di configurazione, tutti uguali, tranne per l'aggiunta di una chiave width e di un valore in pixel:

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.default = function() {
  return src('./src-img/*')
    .pipe(respimg({
    '*': [{
            width: 1000,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-1000' }
            },
            {
            width: 800,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-800' }
            },
            {
            width: 400,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-400' },
        }]
        })
    )
    .pipe(dest('./img/'));
}

Nel caso dell'esempio precedente, le dimensioni dell'immagine originale (monarch.png) erano superiori a 3,3 MB. Il file più grande generato da questa attività (monarch-1000.jpeg) è di circa 150 kB. Il più piccolo, monarch-400.web, ha una dimensione di soli 32 kB.

[10:30:54] Starting 'default'...
[10:30:54] gulp-responsive: monarch.png -> monarch-400.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-800.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-400.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-800.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.webp
[10:30:54] gulp-responsive: Created 6 images (matched 1 of 1 image)
[10:30:54] Finished 'default' after 374 ms

Ovviamente dovrai esaminare attentamente i risultati per verificare la presenza di artefatti di compressione visibili o aumentare la compressione per ottenere ulteriori risparmi. Poiché si tratta di un'attività non distruttiva, queste impostazioni possono essere modificate facilmente.

Nel complesso, in cambio dei pochi kilobyte che potresti ritagliarti con un'attenta micro-ottimizzazione manuale, ottieni un processo non solo efficiente, ma resiliente, uno strumento che applica senza problemi la tua conoscenza degli asset immagine ad alte prestazioni a un intero progetto, senza alcun intervento manuale.

Markup delle immagini adattabile nella pratica

Il completamento degli attributi srcset è in genere una procedura manuale semplice, poiché l'attributo acquisisce in realtà solo le informazioni sulla configurazione che hai già eseguito durante la generazione delle origini. Nelle attività precedenti, abbiamo stabilito i nomi dei file e le informazioni sulla larghezza che il nostro attributo seguirà:

srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w"

Ricorda che i contenuti dell'attributo srcset sono descrittivi, non prescrittivi. Non c'è alcun danno nel sovraccaricare un attributo srcset, a condizione che le proporzioni di ogni origine siano coerenti. Un attributo srcset può contenere l'URI e la larghezza di ogni taglio alternativo generato dal server senza causare richieste inutili. Inoltre, maggiore è il numero di origini candidati fornite per un'immagine visualizzata, più efficiente sarà il browser in grado di personalizzare le richieste.

Come hai appreso nella sezione Immagini adattabili, dovrai utilizzare l'elemento <picture> per gestire senza problemi il pattern di riserva WebP o JPEG. In questo caso, utilizzerai l'attributo type insieme a srcset.

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

Come hai imparato, i browser che supportano WebP riconosceranno i contenuti dell'attributo type e selezioneranno l'attributo srcset dell'elemento <source> come elenco di immagini candidati. I browser che non riconoscono image/webp come tipo multimediale valido ignoreranno questo <source> e utilizzeranno invece l'attributo srcset dell'elemento <img> interno.

C'è un'altra considerazione da fare in termini di supporto dei browser: i browser che non supportano il markup delle immagini adattabili avranno comunque bisogno di un elemento di riserva oppure potremmo correre il rischio di un'immagine non funzionante in contesti di navigazione soprattutto vecchi. Poiché <picture>, <source> e srcset sono tutti ignorati in questi browser, dobbiamo specificare una sorgente predefinita nell'attributo src interno di <img>.

Poiché lo ridimensionamento di un'immagine verso il basso è visivamente senza interruzioni e la codifica JPEG è universalmente supportata, il formato JPEG più grande è una scelta sensata.

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img src="filename-1000.jpg" srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

sizes può essere un po' più difficile da gestire. Come hai imparato, sizes è necessariamente contestuale: non puoi completare l'attributo senza conoscere la quantità di spazio che l'immagine è destinata a occupare nel layout visualizzato. Per richieste quanto più efficienti possibili, deve essere presente un attributo sizes preciso nel nostro markup al momento in cui tali richieste vengono effettuate dall'utente finale, molto prima che vengano richiesti gli stili che regolano il layout della pagina. L'omissione di sizes del tutto non solo costituisce una violazione della specifica HTML, ma comporta anche un comportamento predefinito equivalente a sizes="100vw", che comunica al browser che questa immagine è vincolata solo dall'area visibile stessa e che viene selezionata la selezione delle origini candidati più grandi.

Come nel caso di attività di sviluppo web particolarmente gravose, sono stati creati diversi strumenti per astrarre il processo di scrittura a mano libera degli attributi sizes. respImageLint è uno snippet di codice assolutamente essenziale destinato a verificare l'accuratezza degli attributi sizes e fornire suggerimenti per migliorarli. Viene eseguito come un bookmarklet, uno strumento da eseguire nel browser mentre rimanda alla pagina completamente visualizzata contenente gli elementi immagine. In un contesto in cui il browser ha piena comprensione del layout della pagina, avrà anche una consapevolezza quasi impeccabile dello spazio che un'immagine è in grado di occupare nel layout a ogni possibile dimensione dell'area visibile.

Report sulle immagini adattabili che mostra una mancata corrispondenza di dimensioni/larghezza.

Uno strumento per l'analisi tramite lint degli attributi sizes è sicuramente utile, ma ha ancora più valore come strumento per generarli all'ingrosso. Come sai, la sintassi srcset e sizes è pensata per ottimizzare le richieste di asset immagine in modo visivamente semplice. Anche se non dovrebbe essere mai utilizzato in produzione, un valore segnaposto sizes predefinito di 100vw è del tutto ragionevole mentre lavori sul layout di una pagina nel tuo ambiente di sviluppo locale. Una volta che gli stili di layout sono stati implementati, l'esecuzione di respImageLint ti fornirà attributi sizes personalizzati che puoi copiare e incollare nel tuo markup, a un livello di dettaglio molto maggiore di uno scritto manualmente:

Report sulle immagini adattabili con dimensioni suggerite.

Anche se le richieste di immagine avviate dal markup sottoposto a rendering dal server avvengono troppo rapidamente per consentire a JavaScript di generare un attributo sizes lato client, lo stesso ragionamento non si applica se queste richieste vengono avviate lato client. Il progetto Lazysizes, ad esempio, ti consente di posticipare completamente le richieste di immagine fino a quando il layout non è stato definito, consentendo a JavaScript di generare i nostri valori sizes per noi: una grande comodità per te e una garanzia di richieste il più efficienti possibili per i tuoi utenti. Tieni presente, tuttavia, che questo approccio significa sacrificare l'affidabilità del markup sottoposto a rendering dal server e le ottimizzazioni della velocità integrate nei browser e l'avvio di queste richieste solo dopo il rendering della pagina avrà un impatto negativo enorme sul punteggio LCP.

Ovviamente, se utilizzi già un framework di rendering lato client come React o Vue, questo è un debito che dovrai già incorrere e, in quei casi, l'utilizzo di Lazysizes significa che i tuoi attributi sizes possono essere quasi completamente astratti. Meglio ancora: man mano che l'opzione sizes="auto" per le immagini con caricamento lento ottiene consenso e implementazioni native, lazysize diventerà di fatto un polyfill per il comportamento del browser appena standardizzato.