Ottimizzare il caricamento di Javascript9 min read

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.
Ragazzino meravigliato da cose che lo circondano
Reazione tipica usando un sito GSAP powered
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

  1. Utilizzando
    async
    o
    defer
    sugli scripts #
  2. Anticipando la connessione agli host di origine con
    preconnect
    e
    dns-prefetch
    #
  3. Lazy load #
  4. Usando un CDN #

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
async
o
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
async
subito 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.
Fasi di caricamento di uno script async in javascript
Caricamento di uno script async in Javascript

Defer

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

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.
Phillis della serie tv The Office chiede quanto vino sia disponibile al cameriere
Forget about lasagna. Ask the real question
Gli attributi
preconnect
e
dns-prefetch
del 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">
preconnect
informa 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
preconnect
andrebbe 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-prefetch
gestisce solo una piccola parte delle operazioni che
preconnect
svolge. 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:
preconnect
gode di un supporto minore,
dns-prefetch
invece, risulta più supportato. Per questo motivo,
dns-prefetch
può 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.
Schermata di una pagina web mostra contenuti above the fold e contenuti nascosti
Lazy-load Javascript in pratica
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

Spongebob aspetta seduto in un diner con una tazza di caffè
Dev: Non uso un CDN perché è difficile da settare.
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
async
o
defer
e 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

Bambina confusa
La mia prima volta con un 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

AsyncDefer
Scripts non eseguiti in ordine di codiceScripts eseguiti nell’ordine di codice
Potrebbe bloccare il parsing HTMLNon blocca il parsing HTML
Blocca il rendering HTMLNon 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
defer
rispetto ad
async
. Occhio ai casi in cui l’attributo
defer
venga 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
.
TwitterFacebookLinkedIn
FollowIns.Lin.
...

This is a unique website which will require a more modern browser to work!

Please upgrade today!