Priorizzare le risorse: fare in modo che il browser ti sia d'aiuto

Non tutti i byte inviati dal cavo al browser hanno lo stesso livello di importanza ed il browser lo sa. I browser hanno delle euristiche che tentano di individuare le risorse più importanti da caricare prima - come ad esempio CSS prima di script ed immagini.

Detto questo, come in ogni euristica, non sempre funziona; il browser potrebbe prendere la decisione sbagliata, di solito perché non dispone di informazioni sufficienti in quel momento. Questo articolo spiega come influenzare la priorità del contenuto adeguatamente nei moderni browser facendo loro sapere di cosa avrai bisogno più avanti.

Priorità predefinite nel browser

Come accennato in precedenza, il browser assegna diverse priorità relative a diversi tipi di risorse in base a quanto possano essere critici. Così, ad esempio, un tag <script> in <head> della pagina verrà caricato in Chrome a priorità Alta (dopo i CSS ad Altissima), ma quella priorità cambierebbe in Bassa se ha l'attributo async (significa che può essere caricato ed eseguito in modo asincrono).

Le priorità diventano importanti quando esaminano le prestazioni di caricamento del tuo sito. Al di là delle solite tecniche di misura e analisi del percorso di rendering critico, è utile conoscere le priorità di Chrome per ogni risorsa. Puoi trovarle all'interno del pannello di rete in Chrome Developer Tools. Ecco come si presenta:

Un esempio di come vengono visualizzate le priorità in Chrome Developer Tools
Figura 1: Priorità in Chrome Developer Tools. Potrebbe essere necessario attivare la colonna Priorità facendo clic con il pulsante destro del mouse sulle intestazioni di colonna.

Queste priorità ti danno un'idea dell'importanza relativa ad ogni attributo del browser per ciascuna risorsa. Ricorda che le differenze sottili sono sufficienti perché il browser assegni una priorità diversa; ad esempio, un'immagine che fa parte del rendering iniziale è maggiormente prioritaria rispetto ad un'immagine che inizia fuori dallo schermo. Se vuoi approfondire le priorità, questo articolo di Addy Osmani approfondisce l'argomento dello stato attuale delle priorità in Chrome.

Allora, cosa puoi fare se trovi delle risorse contrassegnate da una priorità diversa da quella che desideri?

Questo articolo esamina tre diverse soluzioni dichiarative, tutte relative ai nuovi tipi <link>. Se le risorse sono fondamentali per l'esperienza utente, ma vengono caricate a una priorità troppo bassa, puoi provare a risolvere questo problema in uno dei due modi: preload o preconnect. Se invece preferisci che sia il browser a recuperare le risorse solo quando ha terminato di gestire il resto prova prefetch.

Esaminiamo tutte e tre!

Preload

<link rel="preload"> informa il browser che una risorsa è necessaria come parte della navigazione corrente, e che dovrebbe iniziare ad essere recuperata il prima possibile. Ecco come usarla:

<link rel="preload" as="script" href="super-important.js">
<link rel="preload" as="style" href="critical.css">

La maggior parte di questo codice è probabilmente quello che ti aspetteresti, tranne forse per l'attributo "as". Ciò consente di informare il browser del tipo di risorsa che si sta caricando, in modo che possa essere gestita correttamente. Il browser non utilizza la risorsa precaricata se non viene impostato il tipo corretto. La risorsa è caricata con la stessa priorità, ma ora il browser lo sa in anticipo, permettendo al download di iniziare prima.

Si può notare che <link rel="preload"> è un'istruzione obbligatoria per il browser; a differenza degli altri suggerimenti sulle risorse di cui parleremo, è qualcosa che il browser deve fare, piuttosto che un semplice suggerimento opzionale. È particolarmente importante testare con attenzione questo aspetto, per assicurarsi di non recuperare accidentalmente due volte lo stesso oggetto o di recuperare qualcosa che non è necessario.

Le risorse che vengono scaricate usando <link rel="preload">, ma non vengono utilizzate dalla pagina corrente entro 3 secondi attiveranno un avviso nella Console di Chrome Developer Tools, quindi assicurati di tenerle sott'occhio!

Un esempio di errore di timeout del preload in Chrome Developer Tools

Caso d'uso: Fonts

I font sono un ottimo esempio di risorse scoperte in ritardo che devono essere recuperate, spesso al termine di uno dei numerosi file CSS caricati da una pagina.

Per ridurre la quantità di tempo che l'utente deve attendere per il contenuto testuale del tuo sito e per evitare fastidiosi flash tra i font di sistema e quelli preferiti puoi usare <link rel="preload"> nel tuo HTML per far sapere immediatamente al browser che è necessario un font.

<link rel="preload" as="font" crossorigin="crossorigin" type="font/woff2" href="myfont.woff2">

Si noti che l'uso di crossorigin qui è importante; senza questo attributo, il font precaricato viene ignorato dal browser e viene eseguito un nuovo recupero. Questo perché i caratteri dovrebbero essere recuperati in modo anonimo dal browser, e la richiesta di preload è resa anonima solo usando l'attributo crossorigin.

Caso d'uso: Percorso critico CSS e JavaScript

Quando si parla di performance della pagina, un concetto utile è il "percorso critico". Il percorso critico si riferisce alle risorse che devono essere scaricate prima del rendering iniziale. Queste risorse, come i CSS, sono fondamentali per ottenere i primi pixel sullo schermo dell'utente.

In precedenza, la raccomandazione consisteva nell'integrare questo contenuto all'interno del codice HTML. Tuttavia, in uno scenario di rendering multi-pagina o lato server questo porta al rapido spreco di molti byte. Rende anche più difficile il controllo delle versioni, poiché qualsiasi modifica nel codice critico invalida qualsiasi pagina in cui è stata inserita.

<link rel="preload"> consente di mantenere i vantaggi del controllo delle versioni dei singoli file e della cache, fornendo allo stesso tempo il meccanismo per richiedere la risorsa il prima possibile.

<link rel="preload" as="script" href="super-important.js">
<link rel="preload" as="style" href="critical.css">

Con preload c'è uno svantaggio: sei ancora soggetto ad un ulteriore roundtrip. Questo roundtrip extra deriva dal fatto che il browser deve prima recuperare il file HTML, e solo allora scopre le risorse successive.

Un modo per aggirare il roundtrip extra è quello di utilizzare HTTP/2 push, per trasportare preventivamente le risorse critiche all'interno della stessa connessione con cui si invia l'HTML. Ciò garantisce che non vi siano tempi di inattività tra il browser dell'utente che recupera l'HTML e l'avvio del download delle risorse critiche. Stai attento però quando utilizzi HTTP/2 push però, dato che è un modo molto forzato per controllare l'utilizzo della larghezza di banda dell'utente ("server knows best") perché lascia al browser poco spazio per prendere le proprie decisioni come non recuperare uno stesso file già nella sua cache!

Preconnect

<link rel="preconnect"> informa il browser che la tua pagina intende stabilire una connessione con un'altra origine e che vorresti che il processo iniziasse il prima possibile.

Stabilire connessioni spesso richiede tempo con reti lente, in particolare quando si tratta di connessioni sicure, poiché potrebbe comportare ricerche DNS, reindirizzamenti e diversi roundtrip sul server finale che gestisce la richiesta dell'utente. Prendersi cura di tutto ciò in anticipo può rendere la tua applicazione molto più accattivante per l'utente senza influire negativamente sull'utilizzo della larghezza di banda. La maggior parte del tempo per stabilire una connessione è speso in attesa, piuttosto che per scambiare dati.

Per informare il browser della tua intenzione basta aggiungere un tag link alla tua pagina:

<link rel="preconnect" href="https://example.com">

In questo caso, stiamo facendo sapere al browser che intendiamo connetterci a example.com e recuperare i contenuti da lì.

Ricorda che mentre <link rel="preconnect"> è piuttosto economico, può comunque richiedere tempo prezioso per la CPU, in particolare su connessioni sicure. Ciò è particolarmente grave se la connessione non viene utilizzata entro 10 secondi, poiché il browser la chiude, sprecando tutto il lavoro di connessione iniziale.

In generale, prova ad usare <link rel="preload"> ovunque sia possibile, in quanto è una modifica più completa delle prestazioni, ma tieni <link rel="preconnect"> nella tua cassetta degli strumenti per i casi limite. Diamo un'occhiata ad un paio di questi.

Caso d'uso: Conoscere Da Dove, ma non Cosa recuperi

A causa delle dipendenze con versioni, a volte finisci in una situazione in cui sai che recupererai una risorsa da un dato CDN, ma non il percorso esatto. In altri casi, è possibile recuperare una delle numerose risorse, in base alle media query o ai controlli delle funzionalità di runtime sul browser dell'utente.

In queste situazioni, se la risorsa che stai recuperando è importante, potresti voler risparmiare il maggior tempo possibile pre-connettendoti al server. Il browser non inizierà il recupero del file prima che ne abbia bisogno (ovvero, una volta che la richiesta viene effettuata dalla tua pagina in qualche modo), ma almeno sei in grado di gestire gli aspetti della connessione in anticipo, salvando l'utente dall'attesa di diversi roundtrip.

Caso d'uso: Streaming Media

Un altro esempio in cui si potrebbe desiderare di risparmiare un po' di tempo nella fase di connessione, ma non necessariamente iniziare immediatamente a recuperare il contenuto, è lo streaming multimediale da un'origine diversa.

A seconda di come la tua pagina gestisce il contenuto in streaming, potresti voler aspettare che gli script siano caricati e pronti per elaborare il flusso. Preconnect ti aiuta a ridurre i tempi di attesa per un singolo roundtrip una volta che sei pronto per iniziare il recupero.

Prefetch

<link rel="prefetch"> è piuttosto diverso da <link rel="preload"> e <link rel="preconnect">, nel senso che non tenta di far succedere qualcosa di critico più velocemente; invece, cerca di far succedere qualcosa di non critico prima, se possibile.

Lo fa informando il browser di una risorsa che dovrebbe essere necessaria come parte di una futura navigazione o interazione dell'utente, ad esempio, qualcosa che potrebbe essere necessario in seguito, se l'utente intraprende l'azione ci aspettiamo. Queste risorse vengono recuperate con la priorità più bassa in Chrome, quando viene caricata la pagina corrente e c'è larghezza di banda disponibile.

Ciò significa che prefetch è più adatto a prevedere ciò che l'utente potrebbe fare in seguito e prepararsi, come recuperare la prima pagina dei dettagli del prodotto in un elenco di risultati o recuperare la pagina successiva in un contenuto impaginato.

<link rel="prefetch" href="page-2.html">

Tieni comunque presente che prefetch non funziona in modo ricorsivo. Nell'esempio precedente recupereresti l'HTML ma qualsiasi risorsa di cui abbia bisogno page-2.html non viene scaricata prima del tempo a meno che tu non espliciti prefetch anche su di essa.

Prefetch non funziona come Override

È importante notare che non è possibile utilizzare <link rel="prefetch"> come metodo per ridurre la priorità di una risorsa esistente. Nel seguente HTML, si potrebbe pensare che dichiarare optional.css in un prefetch riduca la sua priorità del successivo <link rel="stylesheet">:

<html>
  <head>
    <link rel="prefetch" href="optional.css">
    <link rel="stylesheet" href="optional.css">
  </head>
  <body>
    Hello!
  </body>
</html>

Tuttavia, questo farà sì che il tuo foglio di stile venga recuperato due volte (anche con un potenziale cache hit la seconda volta), una volta con priorità predefinita Massima e una volta con priorità Più bassa, come prefetch per un recupero successivo:

Uno screenshot di Chrome Developer Tools che mostra optional.css che
              viene recuperato due volte

Il doppio recupero può essere negativo per gli utenti. In questo caso, non solo avrebbero dovuto attendere il CSS che bloccava il rendering, ma avrebbero anche potenzialmente perso larghezza di banda scaricando il file due volte. Ricorda che la loro larghezza di banda potrebbe essere in base al consumo. Assicurati di analizzare a fondo le richieste della tua rete e fai attenzione a quelle doppie!

Altre tecniche e strumenti

<link rel="preload">, <link rel="preconnect"> e <link rel="prefetch"> (ed il bonus <link rel="dns-prefetch">) sono ottimi strumenti per informare in anticipo il browser in modo dichiarativo riguardo a risorse e connessioni ed ottimizzare l'ordine delle attività in relazione alle reali necessità.

Esistono numerosi altri strumenti e tecniche che è possibile utilizzare per modificare la priorità ed i tempi di caricamento delle risorse. Assicurati di leggere HTTP/2 server push; utilizzare IntersectionObserver per il caricamento lazy di immagini ed altri media; evitare CSS che bloccano il render con media query e librerie come loadCSS; e ritardare il recupero, la compilazione e l'esecuzione di JavaScript con async e defer.

Translated by