⌨️ Sviluppo web

Caricare JavaScript e CSS: defer, async e risorse bloccanti

Perché script e CSS possono bloccare la pagina e come usare defer, async, preload e preconnect per mostrare prima il contenuto a chi visita il sito.

Due pagine con lo stesso peso possono apparire sullo schermo in tempi molto diversi. Spesso la differenza non sta in quanti kilobyte vengono scaricati, ma in quando e in che ordine il browser li riceve e li elabora. Capire come il browser costruisce una pagina permette di spostare poche righe nell'HTML e guadagnare secondi percepiti.

Come il browser costruisce la pagina

In modo semplificato, il browser:

  1. scarica l'HTML e inizia ad analizzarlo dall'alto verso il basso, costruendo il DOM;
  2. quando incontra un foglio di stile, lo scarica e costruisce il CSSOM (il modello degli stili);
  3. quando incontra uno script, lo scarica e lo esegue;
  4. combina DOM e CSSOM, calcola il layout e disegna i pixel.

Due tipi di risorse possono fermare questo processo: il CSS e gli script sincroni.

Il CSS blocca il rendering

Il browser non disegna la pagina finché non ha i fogli di stile caricati nell'head. È un comportamento voluto: senza, l'utente vedrebbe per un istante la pagina senza stile e poi un salto improvviso. La conseguenza è che un CSS grande o lento ritarda la comparsa di tutto il contenuto.

Cosa puoi fare:

  • riduci il CSS iniziale: rimuovi le regole inutilizzate e suddividi gli stili per sezione o tipo di pagina;
  • usa gli attributi media: un foglio con media="print" non blocca il rendering dello schermo;
  • valuta il CSS critico: inserisci direttamente nell'HTML le poche regole necessarie alla parte visibile senza scorrere, e carica il resto in seguito. È una tecnica efficace ma complessa da mantenere, da riservare ai casi in cui serve davvero.
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/css/print.css" media="print">

Gli script sincroni bloccano l'analisi

Uno script classico, senza attributi, ferma l'analisi dell'HTML finché non è stato scaricato ed eseguito:

<script src="/js/app.js"></script>

Il motivo storico è che lo script potrebbe modificare il documento con document.write, quindi il browser non può proseguire. Se lo script è nell'head e pesa molto, l'utente fissa una pagina bianca.

defer

<script src="/js/app.js" defer></script>

Con defer lo script viene scaricato in parallelo all'analisi dell'HTML ed eseguito solo alla fine, prima dell'evento DOMContentLoaded. Gli script con defer vengono eseguiti nell'ordine in cui compaiono. È la scelta giusta per quasi tutti gli script dell'applicazione, che hanno bisogno del DOM completo e spesso dipendono l'uno dall'altro.

async

<script src="https://esempio-analytics.it/tag.js" async></script>

Con async lo script viene scaricato in parallelo ed eseguito appena è pronto, interrompendo l'analisi in quel momento. L'ordine di esecuzione non è garantito. È adatto a script indipendenti, che non dipendono da altri e da cui nessuno dipende, come alcuni strumenti di misurazione.

Attributo Download Esecuzione Ordine
nessuno blocca l'analisi subito sì
defer in parallelo dopo l'analisi dell'HTML sì
async in parallelo appena scaricato no
type="module" in parallelo come defer sì

Gli script di tipo modulo sono differiti per impostazione predefinita; puoi aggiungere async anche a un modulo se vuoi che venga eseguito appena pronto.

Suggerimenti di priorità: preload, preconnect e fetchpriority

Il browser scopre le risorse man mano che analizza l'HTML e i CSS. Alcune risorse importanti, però, vengono scoperte tardi: un font citato dentro un CSS, un'immagine di sfondo, uno script caricato da un altro script. I resource hints permettono di anticiparle.

preload

Chiede di scaricare subito una risorsa che sarà sicuramente necessaria:

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

L'attributo as è obbligatorio perché il browser assegni la priorità corretta. Per i font serve crossorigin anche se il file è sullo stesso dominio. Non abusarne: tutto ciò che precarichi compete per la banda con il resto, e precaricare risorse che poi non vengono usate produce un avviso nella console e uno spreco.

preconnect

Apre in anticipo la connessione verso un altro dominio da cui caricherai risorse a breve, risparmiando il tempo di risoluzione del nome e di negoziazione della connessione sicura:

<link rel="preconnect" href="https://cdn.esempio.it">

Limitalo a uno o due domini davvero critici; per gli altri esiste dns-prefetch, più leggero.

fetchpriority

Indica la priorità relativa di una risorsa rispetto alle altre dello stesso tipo:

<img src="/img/copertina.webp" fetchpriority="high" alt="...">
<script src="/js/widget-chat.js" async fetchpriority="low"></script>

Gli script di terze parti

Widget di chat, pulsanti social, mappe, strumenti pubblicitari e di tracciamento sono spesso la causa principale di pagine lente, e sfuggono al tuo controllo. Qualche strategia:

  • carica solo ciò che serve davvero e rivedi periodicamente l'elenco;
  • usa sempre async o defer;
  • per componenti pesanti come video o mappe, mostra un'anteprima statica e carica il componente vero solo al clic;
  • valuta l'impatto con il pannello Prestazioni degli strumenti per sviluppatori, bloccando temporaneamente un dominio per vedere quanto cambia.

Come individuare le risorse bloccanti

Lighthouse segnala le risorse che bloccano il rendering con una voce dedicata. Nel pannello Rete puoi osservare la cascata delle richieste: una lunga catena in cui ogni risorsa inizia solo quando la precedente è finita indica una scoperta tardiva, risolvibile con preload o spostando i riferimenti nell'HTML.

In sintesi

  • Il CSS nell'head blocca il rendering: mantienilo piccolo e usa media per gli stili non essenziali.
  • Gli script senza attributi bloccano l'analisi dell'HTML: usa defer per gli script dell'applicazione.
  • async è adatto solo a script indipendenti, perché l'ordine non è garantito.
  • preload, preconnect e fetchpriority anticipano le risorse critiche, se usati con parsimonia.
  • Gli script di terze parti vanno ridotti, caricati in modo asincrono o solo su richiesta.
MM
Marco Marzaduri

Marco Marzaduri si occupa di cybersecurity, informatica forense e sviluppo web. Laureato in informatica, unisce il rigore dell'analisi tecnica all'attenzione per le persone che usano la tecnologia.

Leggi anche