Oggi parleremo di alcune tecniche di ottimizzazione molto utili per aumentare le performance quando si fa uso di risorse esterne, ovvero sempre.
Vedremo come ridurre al minimo alcuni tempi di latenza di rete e come priorizzare il download di risorse critiche a prescindere dalla loro posizione nel codice HTML.
In particolare parleremo di pre-fetching, un modo per suggerire al browser le risorse che verranno utilizzate o che potrebbero essere utilizzate in futuro. Alcuni di questi suggerimenti si applicano alla pagina attuale, altri a possibili pagine future.
Tutte le seguenti tecniche si applicano mediante l’utilizzo di tag HTML <link> che vanno posizionati tra i tag <head> della pagina web.
Indice dei contenuti
Ogni qual volta il browser richiede la visualizzazione di una pagina o il download di una risorsa, qualsiasi essa sia (immagine, script, CSS, font, video..), dovrà stabilire una connessione con il server di origine.
Per poter stabilire una connessione, qualsiasi client dovrà in prima battuta convertire il DNS (il nome del sito) in indirizzo IP. Per farlo avvia una richiesta di risoluzione DNS al sistema operativo.
Il sistema operativo controlla se possiede già in memoria il corrispettivo DNS<->Indirizzo IP. Se non lo possiede, ne fa esplicita richiesta ai server DNS sparsi sul web.
Ricevuta la risposta la comunicherà al client che ne ha fatto richiesta il quale potrà finalmente iniziare a stabilire una connessione.
Naturalmente tutto questo comporta del tempo e va fatto ogni qual volta si intende stabilire una connessione con un server diverso. Quasi sempre nelle moderne applicazioni web si fa riferimento a risorse ospitate su server esterni (Web Fonts, Google Analytics, Adsense, social buttons..).
Dall’immagine sottostante si nota come 8 delle prime 15 richieste per visualizzare il sito di Salvatore Aranzulla fanno capo a server web diversi e di conseguenza occorre aprire 8 connessioni e risolvere 8 DNS.
La tecnica dns-prefetch viene utilizzata per risolvere in background il DNS della URL specificata nell’attributo href:
<link rel="dns-prefetch" href="//example.com">
Grazie a questa direttiva, il browser nei momenti di inattività dovrebbe risolvere il DNS, accelerando dunque il processo di connessione quando verrà richiesto il download di una risorsa da //example.com.
È il browser a decidere se onorare o meno questa richiesta.
La sola risoluzione DNS non basta per stabilire una connessione, in realtà serve solo per individuare l’indirizzo IP del server con il quale dialogare.
Per stabilire una connessione viene utilizzato il protocollo TCP ed effettuato il TCP handshake. Se la connessione avviene in HTTPS, oltre al TCP handshake viene effettuata la negoziazione TLS.
Naturalmente sono operazioni che richiedono tempo e dipendono strettamente dalla lontananza fisica del server rispetto al client, dalla sua velocità ed altri fattori. Nell’esempio sottostante, la sola connessione presso il server web di Salvatore Aranzulla richiede ben 282 millisecondi.
La tecnica preconnect è simile alla dns-prefetch. A differenza di quest’ultima non si limita solo a risolvere il DNS ma ad instaurare una vera e propria connessione.
Quindi viene risolto il DNS, viene effettuato il TCP handshake e, in caso di HTTPS, viene completata la negoziazione TLS. Preconnect è molto utile per quelle risorse che potrebbero presentare un’elevata latenza di connessione.
<link rel="preconnect" href="//example.com">
Come per dns-prefetch, è il browser a decidere se e quando effettuare la pre-connessione. Potrebbe effettuarla solo parzialmente o evitarla del tutto a seconda del carico di lavoro o del numero di connessioni parallele già aperte, il cui valore massimo cambia da browser a browser.
Salvatore Aranzulla ne fa uso sul suo sito per accelerare i processi di connessione a Google Tag Manager e Adsense:
<link rel="dns-prefetch preconnect" href="https://www.googletagservices.com"> <link rel="dns-prefetch preconnect" href="https://securepubads.g.doubleclick.net"> <link rel="dns-prefetch preconnect" href="https://tpc.googlesyndication.com">
Dal grafico di WebPageTest lo si può notare in quanto la connessione parte in un momento differente rispetto al download della risorsa in sé:
Qualora fosse richiesto, è possibile inserire un terzo attributo per le regole CORS: crossorigin.
<link rel="preconnect" href="//example.com" crossorigin>
Mentre le prime due tecniche mirano ad accelerare i processi relativi alla connessione, questa e la prossima tecnica mirano ad accelerare il download di risorse critiche.
Con prefetch si identifica una risorsa che potrebbe essere richiesta durante la navigazione e che il browser dovrebbe scaricare quanto prima affinché questa sia già disponibile quando richiesta.
<link rel="prefetch" href="/script.js" as="script">
È sempre il browser a decidere se dare o meno ascolto al suggerimento a procedere al download. È possibile utilizzare l’attributo as per specificare il contesto di utilizzo della risorsa (font, script, image, style) affinché il browser possa settare una priorità.
Script e CSS hanno priorità diverse a seconda se vengono richiesti prima o dopo il processo di rendering, così come le immagini hanno una priorità diversa rispetto ai font.
Prefetch va utilizzato per quelle risorse non critiche ma che con una buona probabilità verranno utilizzate nel corso della navigazione. Ad esempio le risorse subito utilizzate allo scattare dell’evento onLoad.
Anche in questo caso è possibile utilizzare l’attributo crossorigin.
<link rel="prefetch" href="//example.com/pagina-2.html" as="html" crossorigin="use-credentials">
Le risorse statiche scaricate con prefetch vengono inserite nella browser cache come qualsiasi altra risorsa.
Preload è molto simile a prefetch ma in questo caso il browser non può decidere di ignorare la richiesta e deve procedere al download della risorsa.
Data la sua natura va utilizzata solo ed esclusivamente per le risorse critiche: Web Fonts, script essenziali, immagini hero, CSS necessari.
<link rel="preload" href="/script.js" as="script">
Anche in questo caso le risorse statiche scaricate vengono inserite nella browser cache come qualsiasi altra risorsa. Per aumentarne l’efficienza è consigliato indicare tramite l’header HTTP link la risorsa critica da scaricare. Di seguito un esempio di come è possibile farlo in PHP:
<?php function pushImage($uri) { header("Link: <{$uri}>; rel=preload; as=image", false); return <<<HTML <img src="{$uri}"> HTML; } $image1 = pushImage("/images/drucken.jpg"); $image2 = pushImage("/images/empire.jpg"); ?>
Naturalmente in questo caso non occorre più avere il riferimento sotto forma di tag HTML.
Prerender viene utilizzato per identificare una risorsa che potrebbe essere richiesta in una navigazione successiva. Grazie a prerender il browser potrebbe effettuare il fetching di tale risorsa affinché sia già disponibile al momento della richiesta.
Questa tecnica può essere utilizzata anche per effettuare il pre-fetching di una pagina HTML. Molto utile nei casi di pagine in stile wizard o con paginazione frequentemente utilizzata.
<link rel="prerender" href="//example.com/page-2.html">
Se non utilizzati propriamente, prefetch e preload posso causare un double fetch della risorsa, ovvero la risorsa viene scaricata due volte con priorità diverse. Quindi anziché ottimizzare l’applicazione l’avremo rallentata.
Per visualizzare le priorità dalla DevTools di Google Chrome, cliccare sul tab Network e successivamente con il tasto destro sulle colonne della tabella e spuntare la voce Priority.
Per evitare i double fetch utilizzare sempre l’attributo as e non utilizzare mai prefetch e preload per la stessa risorsa:
I font sono un caso particolare di risorse critiche. Essi vengono scaricati solo quando occorre stampare un testo che li utilizza. Se non presenti, il browser potrebbe non stampare alcun testo, il famoso effetto blank text, oppure stamparlo con un font diverso per poi applicare il nuovo quando disponibile.
In entrambi i casi l’esperienza utente è negativa. Per evitare di ritardare il tempo di visualizzazione corretto del testo è possibile utilizzare preload per scaricare i font con elevata priorità.
Attenzione! Per evitare double fetch, i font vanno scaricati facendo uso dell’attributo crossorigin anche se questi sono ospitati sullo stesso server del sito web.
<link rel="preload" as="font" crossorigin="crossorigin" type="font/woff2" href="myfont.woff2">
Di seguito un esempio di come Shopify ha accelerato del 50% il tempo di visualizzazione del testo usando la tecnica preload.