Sommario: ottimizzare il caricamento di scripts Javascript di terze parti per ridurre i tempi di caricamento del tuo sito web e migliorare l’esperienza utente.
Per deformazione (professionale) quasi ogni giorno visito gli showcase dei progetti web migliori del globo. Ed oltre a sviluppare una forma grave di depressione, non posso non notare come Javascript spinga un’enorme quantità di questi progetti, non soltanto nelle funzioni, ma anche (e soprattutto) nel layout. Siti web in tutto e per tutto powered by Javascript. Più o meno tutti sono abbastanza veloci. Considerando la mole di scripts che usano, non è niente male.

Avendo compreso che abbandonarmi all’autocommiserazione non migliora il punteggio PageSpeed del mio sito, ho deciso di capire in che modo ottimizzare il caricamento di scripts Javascript di terze parti e guadagnare qualche millisecondo in più sui tempi di caricamento.
Come ottimizzare il caricamento di Javascript
Usare async o defer
Per spiegare questo punto soffermiamoci brevemente su come avvenga il “display” (lasciatemi passare il termine) del nostro sito a schermo. L’operazione è naturalmente molto complessa e avviene in diverse fasi: quella cruciale per noi è la costruzione del DOM. Durante questa fase, il browser determina gli elementi che andranno a comporre la nostra pagina. Durante il parsing, le risorse Javascript e CSS potrebbero bloccare il processo sinché non vengono scaricate. Il condizionale è d’obbligo perché questo può essere ottimizzato.
Specificando che il caricamento di Javascript avvenga in modo asincrono possiamo evitare che venga bloccato il processo di costruzione e rendering del DOM, migliorando notevolmente i tempi di caricamento. Com’è possibile? Dicendo al browser che il caricamento degli scripts debba avvenire attraverso l’attributo
asynco
defer, stiamo comunicandogli di continuare il parsing HTML e di scaricare gli scripts in background e di eseguirli in seguito. In questo modo non bloccheranno la costruzione del DOM e il rendering della pagina. La differenza tra i due attributi sta nel momento in cui iniziano a eseguire gli scripts.
Async
<script async src="script.js">Iniziamo applicando l’attributo
asyncsubito dopo l’apertura del tag script. Uno script con attributo
async, una volta terminato il download, inizierà l’esecuzione al primo momento utile. Questo vuol dire che è molto probabile che lo script (o gli scripts) non verranno eseguiti nell’ordine in cui appaiono nel codice HTML, perché il download di uno script può terminare prima di un altro e dunque iniziare la sua esecuzione subito. Questo vuol anche dire che gli scripts potrebbero interrompere la costruzione del DOM se il download termina prima che questo abbia terminato il suo lavoro.

Defer
<script defer src="script.js">Uno script scaricato in
deferverrà eseguito solo quando il parsing sarà completamente terminato. Garantisce inoltre che gli scripts vengano eseguiti nell’ordine in cui appaiono nel documento HTML.

Quando usare l’uno o l’altro?
- usare
async
per scripts critici ed importanti - usare
defer
per scripts secondari, che per esempio riguardano elementi below the fold
Anticipare la connessione agli host di origine
Immaginate di avere una cena tra amici. Immaginate che la portata principale sia la lasagna della nonna. Ci vuole tempo. Quindi da bravi padroni di casa (e da persone dotate di buon senso) mettete in forno la lasagna prima degli antipasti. In questo modo anticipate il tempo di cottura per servirla agli ospiti al momento giusto.

Gli attributi
preconnecte
dns-prefetchdel tag link svolgono proprio questa funzione. Questa modifica ci consente di risparmiare dai 100 ai 500 ms.
Preconnect
<link rel="preconnect" as="script" href="megaimportante.js">
preconnectinforma il browser che la pagina intende stabilire una connessione ad un host di origine il prima possibile. Quando la risorsa richiesta dall’host in pre-connessione è disponibile, il download inizia subito. Bisogna notare che
preconnectandrebbe utilizzato solo per le risorse critiche che verranno utilizzate subito, perché il browser chiuderà le connessioni che non avvengono entro 10 secondi. Se una risorsa impiega più di 10 secondi per essere scaricata, ritarderà le richieste successive impattando sui tempi di caricamento, costruzione e rendering della pagina.
Dns-prefetch
Attenzione per questo.
<link rel="dns-prefetch" as="script" href="menomegaimportante.js">
dns-prefetchgestisce solo una piccola parte delle operazioni che
preconnectsvolge. Quando si stabilisce una connessione ad un host, viene risolta anzitutto la fase di DNS lookup (si cerca l’host che corrisponde a un dato nome dominio), e successivamente il TCP handshake (si stabilisce la connessione al server), e solo per le origini sicure (quelle cioè che hanno certificato di sicurezza TLS), la relativa negoziazione. Torniamo a
dns-prefetch. Questo svolge soltanto la prima delle operazioni descritte, cioè la risoluzione DNS dell’origine fornita. Fin.
Quando usiamo l’uno o l’altro?
-
preconnect
per le connessioni critiche e più importanti -
dns-prefetch
per le secondarie
Attenzione però. Il supporto per questi due metodi è diverso:
preconnectgode di un supporto minore,
dns-prefetchinvece, risulta più supportato. Per questo motivo,
dns-prefetchpuò essere utilizzato come fallback per i browser che non supportano
preconnect.
<link rel="preconnect" as="script" href="script.js"> <link rel="dns-prefetch" as="script" href="script">
Lazy-load Javascript
Per chi non la conoscesse, il lazy-load è una tecnica di caricamento differito delle risorse di una pagina al momento in cui queste vengono effettivamente richieste. Mi spiego meglio.

I contenuti above the fold (la parte di pagina che è nella viewport, che cioè è visibile) necessitano di essere caricati prima possibile in quanto saranno i primi ad essere visualizzati dall’utente. Tutto ciò che si trova nella parte sottostante (nell’immagine in rosa pesca) che cioè non serve immediatamente poiché non visibile, può essere caricata in un secondo momento. Ed è qui che entra in gioco il lazy-load. Le immagini possono essere scaricate solo quando l’utente, tramite scroll, si troverà a visualizzarle. In questo modo il contenuto è servito solo quando è necessario.
Per Javascript il discorso è analogo. Non funzionerà come per le immagini, perché probabilmente ne avremo bisogno prima. Un ottimo modo di usare il lazy-load Javascript è caricare le risorse quando il contenuto principale della pagina ha terminato il caricamento (la parte above the fold). Questo diminuisce in modo significativo i tempi di caricamento e contribuisce a una migliore esperienza utente.
Attenzione nell’usare il lazy-load con Javascript. Se un qualche problema dovesse occorrere nel codice (o magari ti trovi nella campagna toscana e la tua connessione non è delle migliori), non verrà caricata nessuna risorsa.
Usare un CDN

User:
Molti fornitori di librerie / risorse Javascript esterne utilizzano un CDN (content delivery network) per servire gli scripts al pubblico. Questa soluzione è in realtà estremamente utile, in quanto usa le funzionalità della dislocazione dei server per fornire in maniera più rapida la risorsa in base alla posizione dell’utente che la richiede, migliorando la prestazioni grazie ai tempi di latenza inferiori. Inoltre gli scripts sono sempre aggiornati, perché è il vendor a occuparsi della distribuzione. Già di default questo metodo consente di ottimizzare il caricamento di Javascript sul nostro sito senza ulteriori azioni.
Hostare gli scripts sul proprio server
In alternativa, è possibile hostare gli scripts sul proprio server. In questo modo:
- si riducono i lookup DNS
- si trae beneficio dal protocollo HTTP/2 server push
- usereste la vostra strategia di cache
Hostare gli scripts sul vostro server tuttavia non garantisce che questi siano sempre aggiornati. Dovrete infatti verificare periodicamente se nuove versioni dello script siano state rilasciate, e provvedere ad aggiornarle. Spesso non è un procedimento facile, e a volte può risultare in problemi di compatibilità piuttosto gravi. Accertatevi sempre di seguire le procedure di migrazione indicate dal vendor.
In termini generali, è sempre una buona idea ricorrere a un CDN. Sebbene non sia scevro da problemi, resta comunque una tecnica must have per garantire tempi di caricamento minimi e migliorare le performance (primariamente grazie alla tecnica edge-caching).
I principali servizi di hosting offrono soluzioni CDN integrate nei loro piani.
Kinsta ad esempio (l’hosting che serve il mio sito), dispone di un CDN proprietario con TTFB (time to first byte) molto basso (cioè ha bassa latenza) che integra in tutti i suoi piani.
Difficoltà di configurazione
3,5 / 5
Ottimizzare il caricamento di Javascript con i metodi indicati ha una difficoltà di medio livello. Non basta infatti aggiungere l’attributo
asynco
defere sperare che il main thread non collassi. O impostare il lazy-load e tagliare fuori 1 secondo di caricamento per poi scoprire che le immagini non caricano affatto. Ma una conclusione del tipo “dipende dai casi” non è quello che vogliamo, per cui vediamo di capirci qualcosa.
Ottimizzare il caricamento di Javascript con CDN

Forse una delle operazioni più macchinose, considerando configurazioni varie in mezzo. La prima volta che configurai un CDN sul sito di un cliente ero spaventatissimo. Temevo che se qualcosa fosse andato storto, il sito sarebbe stato irraggiungibile per settimane. In realtà è stato un processo abbastanza semplice, considerando che l’hosting provider di partenza aveva una sezione dedicata al cambio DNS e che varie guide erano disponibili sull’argomento. Non solo: molto spesso sono gli stessi hosting provider a fornirle. Kinsta ha una guida dedicata all’argomento. Molto dipende anche dallo stesso provider: Kinsta, come già menzionato sopra, dispone di un CDN proprietario. Per questo motivo, è bastato premere un tasto per abilitarlo sul mio sito. Niente di più semplice.
Se invece si ricorre a un CDN gratuito (perché l’hosting provider non offre soluzioni integrate), il procedimento è in genere un po’ più lungo. Bisogna infatti cambiare il puntamento DNS del proprio sito per impostare quello indicato dal CDN provider. Ancora una volta, Google vi è amico, e sull’argomento ci sono molteplici guide. Non dimenticate anche che è sempre possibile ricorrere all’assistenza dell’hosting provider. Saranno loro a indicarvi come fare, passo passo. Puntato il DNS, è necessario attendere il tempo di propagazione, che in genere dura sino a un massimo di 48 h. Dalla dashboard del servizio CDN sarà possibile verificare la corretta implementazione. A quel punto, il CDN sarà attivo e funzionante.
Ottimizzare il caricamento Javascript con async o defer
Facciamo un breve recap su cosa fanno questi due attributi.
-
async
inizia l’esecuzione degli script una volta che questi terminano il download -
defer
inizia l’esecuzione degli script sempre dopo il parsing HTML
Async e defer: comparison
Async | Defer |
---|---|
Scripts non eseguiti in ordine di codice | Scripts eseguiti nell’ordine di codice |
Potrebbe bloccare il parsing HTML | Non blocca il parsing HTML |
Blocca il rendering HTML | Non blocca il rendering HTML |
Sebbene in genere sia sempre una buona idea ispezionare il timing DOM del proprio sito, i casi d’uso vedono prevalere l’uso di
deferrispetto ad
async. Occhio ai casi in cui l’attributo
defervenga usato su scripts di grandi dimensioni: in questo caso infatti lo script ci metterà presumibilmente più tempo in download rispetto al tempo che impiega DOMInteractive, e il browser dovrà attendere il download prima di attivare DomContentLoaded. In questi casi infatti,
asyncè consigliato rispetto a
defer.