Ottimizza il caricamento delle risorse

Nel modulo precedente sono state esplorate alcune teorie alla base del percorso di rendering critico e abbiamo illustrato in che modo le risorse di blocco del rendering e dell'analizzatore sintattico possono ritardare il rendering iniziale di una pagina. Ora che conosci alcune delle teorie alla base di questo processo, puoi apprendere alcune tecniche per ottimizzare il percorso di rendering critico.

Durante il caricamento di una pagina, all'interno del codice HTML vengono fatte riferimento a molte risorse che forniscono una pagina con il suo aspetto e layout tramite CSS, oltre che la sua interattività tramite JavaScript. In questo modulo vengono illustrati una serie di concetti importanti relativi a queste risorse e al modo in cui influiscono sul tempo di caricamento di una pagina.

Blocco del rendering

Come discusso nel modulo precedente, il CSS è una risorsa di blocco della visualizzazione, in quanto impedisce al browser di visualizzare i contenuti fino alla creazione del CSS Object Model (CSSOM). Il browser blocca il rendering per impedire il Flash di contenuti senza stile (FOUC), che è indesiderato dal punto di vista dell'esperienza utente.

Nel video precedente è presente un breve FOUC in cui puoi vedere la pagina senza nessuno stile. In seguito, tutti gli stili vengono applicati una volta terminato il caricamento del CSS della pagina dalla rete e la versione senza stile della pagina viene immediatamente sostituita con la versione con stile.

In linea di massima, un FOUC è qualcosa che non si vede normalmente, ma è importante capire questo concetto, in modo da sapere perché il browser blocca il rendering della pagina fino a quando il CSS non viene scaricato e applicato alla pagina. Il blocco del rendering non è necessariamente indesiderato, ma è preferibile ridurre al minimo la durata mantenendo il CSS ottimizzato.

Blocco del parser

Una risorsa di blocco del parser interrompe l'analizzatore sintattico HTML, ad esempio un elemento <script> senza attributi async o defer. Quando il parser rileva un elemento <script>, il browser deve valutare ed eseguire lo script prima di procedere con l'analisi del resto del codice HTML. Questo è stato progettato, poiché gli script possono modificare o accedere al DOM durante un periodo di tempo mentre è ancora in fase di creazione.

<!-- This is a parser-blocking script: -->
<script src="/script.js"></script>

Quando utilizzi file JavaScript esterni (senza async o defer), l'analizzatore sintattico viene bloccato quando il file viene rilevato fino a quando non viene scaricato, analizzato ed eseguito. Quando utilizzi JavaScript incorporato, l'analizzatore sintattico viene bloccato in modo simile finché lo script incorporato non viene analizzato ed eseguito.

Lo scanner di precaricamento

Lo scanner di precaricamento è un'ottimizzazione del browser sotto forma di analizzatore HTML secondario che analizza la risposta HTML non elaborata per trovare le risorse e recuperarle in modo speculativo prima che l'analizzatore sintattico HTML principale le rilevi. Ad esempio, lo scanner di precaricamento consente al browser di avviare il download di una risorsa specificata in un elemento <img>, anche quando l'analizzatore sintattico HTML è bloccato durante il recupero e l'elaborazione di risorse come CSS e JavaScript.

Per sfruttare lo scanner di precaricamento, è necessario includere risorse fondamentali nel markup HTML inviato dal server. I seguenti pattern di caricamento delle risorse non sono rilevabili dallo scanner di precaricamento:

  • Immagini caricate da CSS utilizzando la proprietà background-image. Questi riferimenti alle immagini sono in CSS e non possono essere rilevati dallo scanner di precaricamento.
  • Script a caricamento dinamico sotto forma di markup dell'elemento <script> inserito nel DOM tramite JavaScript o moduli caricati con il metodo dinamico import().
  • HTML sottoposto a rendering sul client mediante JavaScript. Questo markup è contenuto all'interno di stringhe nelle risorse JavaScript e non è rilevabile dallo strumento di scansione del precaricamento.
  • Dichiarazioni @import CSS.

Questi pattern di caricamento delle risorse sono tutte risorse scoperte in ritardo e, pertanto, non traggono vantaggio dallo scanner di precaricamento. Se possibile, evitale. Tuttavia, se evitare questi pattern non è possibile, puoi comunque utilizzare un suggerimento preload per evitare ritardi nel rilevamento delle risorse.

CSS

Il CSS determina la presentazione e il layout di una pagina. Come descritto in precedenza, il CSS è una risorsa che blocca la visualizzazione, pertanto l'ottimizzazione del CSS potrebbe avere un notevole impatto sul tempo di caricamento complessivo della pagina.

Minimizzazione

La minimizzazione dei file CSS riduce le dimensioni dei file di una risorsa CSS, rendendoli più veloci da scaricare. Questo avviene principalmente rimuovendo i contenuti da un file CSS di origine, come spazi e altri caratteri invisibili, e generando il risultato in un file appena ottimizzato:

/* Unminified CSS: */

/* Heading 1 */
h1 {
  font-size: 2em;
  color: #000000;
}

/* Heading 2 */
h2 {
  font-size: 1.5em;
  color: #000000;
}
/* Minified CSS: */
h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}

Nella sua forma più semplice, la minimizzazione dei CSS è un'ottimizzazione efficace che potrebbe migliorare il valore FCP del tuo sito web e, in alcuni casi, anche l'LCP. Strumenti come i bundler possono eseguire automaticamente questa ottimizzazione nelle build di produzione.

Rimuovi il CSS inutilizzato

Prima di eseguire il rendering dei contenuti, il browser deve scaricare e analizzare tutti i fogli di stile. Il tempo necessario per completare l'analisi include anche gli stili non utilizzati nella pagina corrente. Se utilizzi un bundler che combina tutte le risorse CSS in un unico file, è probabile che gli utenti scarichino più CSS del necessario per visualizzare la pagina corrente.

Per trovare codice CSS inutilizzato per la pagina corrente, utilizza lo strumento Copertura in Chrome DevTools.

Uno screenshot dello strumento di copertura in Chrome DevTools. Nel riquadro inferiore viene selezionato un file CSS che mostra una quantità considerevole di CSS non utilizzata dal layout di pagina corrente.
Lo strumento di copertura in Chrome DevTools è utile per rilevare il codice CSS (e JavaScript) non utilizzato dalla pagina corrente. Può essere utilizzato per suddividere i file CSS in più risorse da caricare da pagine diverse, anziché spedire un bundle CSS molto più grande che può ritardare il rendering della pagina.

La rimozione del CSS inutilizzato ha un duplice effetto: oltre a ridurre il tempo di download, stai ottimizzando la struttura della struttura ad albero di rendering, in quanto il browser deve elaborare un numero inferiore di regole CSS.

Evita le dichiarazioni @import CSS

Sebbene possa sembrare pratico, dovresti evitare le dichiarazioni @import in CSS:

/* Don't do this: */
@import url('style.css');

Analogamente al funzionamento dell'elemento <link> in HTML, la dichiarazione @import in CSS ti consente di importare una risorsa CSS esterna dall'interno di un foglio di stile. La principale differenza tra questi due approcci è che l'elemento HTML <link> fa parte della risposta HTML e pertanto viene rilevato molto prima di un file CSS scaricato da una dichiarazione @import.

Il motivo è che, per poter trovare una dichiarazione @import, è necessario prima scaricare il file CSS che la contiene. Ciò si traduce in una catena di richieste che, nel caso di CSS, ritarda il tempo necessario per il rendering iniziale di una pagina. Un altro svantaggio è che i fogli di stile caricati con una dichiarazione @import non possono essere rilevati dallo scanner di precaricamento e, di conseguenza, diventano risorse di blocco della visualizzazione scoperte in ritardo.

<!-- Do this instead: -->
<link rel="stylesheet" href="style.css">

Nella maggior parte dei casi, puoi sostituire @import utilizzando un elemento <link rel="stylesheet">. Gli elementi <link> consentono di scaricare i fogli di stile contemporaneamente e riducono il tempo di caricamento complessivo, a differenza delle dichiarazioni @import, che scaricano i fogli di stile consecutivamente.

CSS critico incorporato

Il tempo necessario per scaricare i file CSS può aumentare il valore FCP di una pagina. L'incorporamento di stili critici nel documento <head> elimina la richiesta di rete per una risorsa CSS e, se eseguita correttamente, può migliorare i tempi di caricamento iniziali quando la cache del browser di un utente non è pronta. Il restante CSS può essere caricato in modo asincrono o aggiunto alla fine dell'elemento <body>.

<head>
  <title>Page Title</title>
  <!-- ... -->
  <style>h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}</style>
</head>
<body>
  <!-- Other page markup... -->
  <link rel="stylesheet" href="non-critical.css">
</body>

Un aspetto negativo è che l'incorporamento di una grande quantità di CSS aggiunge più byte alla risposta HTML iniziale. Poiché spesso le risorse HTML non possono essere memorizzate nella cache per molto tempo o per niente, il CSS incorporato non viene memorizzato nella cache per le pagine successive che potrebbero utilizzare lo stesso CSS in fogli di stile esterni. Testa e misura il rendimento della tua pagina per accertarti che valga la pena di scendere a compromessi.

Demo di CSS

JavaScript

JavaScript genera gran parte dell'interattività sul web, ma ha un costo. L'invio di una quantità eccessiva di codice JavaScript può rallentare la risposta della pagina web durante il caricamento della pagina e persino causare problemi di reattività che rallentano le interazioni, che possono essere frustranti per gli utenti.

JavaScript che blocca il rendering

Durante il caricamento di elementi <script> senza gli attributi defer o async, il browser blocca l'analisi e il rendering finché lo script non viene scaricato, analizzato ed eseguito. Analogamente, gli script incorporati bloccano l'analizzatore sintattico finché lo script non viene analizzato ed eseguito.

async contro defer

async e defer consentono il caricamento di script esterni senza bloccare l'analizzatore sintattico HTML, mentre gli script (inclusi quelli incorporati) con type="module" vengono differiti automaticamente. Tuttavia, async e defer presentano alcune differenze che è importante comprendere.

Una rappresentazione di vari meccanismi di caricamento degli script, tutti con dettagli su parser, recupero ed esecuzione in base ai vari attributi utilizzati, come asincrono, defer, type=&#39;module&#39; e una combinazione di tutti e tre.
Tratto da https://html.spec.whatwg.org/multipage/scripting.html

Gli script caricati con async vengono analizzati ed eseguiti immediatamente dopo il download, mentre gli script caricati con defer vengono eseguiti al termine dell'analisi del documento HTML. Questa operazione si verifica contemporaneamente all'evento DOMContentLoaded del browser. Inoltre, gli script async potrebbero essere eseguiti in ordine errato, mentre quelli defer vengono eseguiti nell'ordine in cui sono visualizzati nel markup.

Rendering lato client

In genere, dovresti evitare di utilizzare JavaScript per eseguire il rendering di contenuti critici o dell'elemento LCP di una pagina. Questo è noto come rendering lato client ed è una tecnica ampiamente utilizzata nelle applicazioni a pagina singola (APS).

Il markup sottoposto a rendering da JavaScript ignora lo scanner di precaricamento, in quanto le risorse contenute nel markup sottoposto a rendering dal client non sono rilevabili da quest'ultimo. Questo potrebbe ritardare il download di risorse fondamentali, come un'immagine LCP. Il browser inizia a scaricare l'immagine LCP solo dopo l'esecuzione dello script e l'elemento viene aggiunto al DOM. A sua volta, lo script può essere eseguito solo dopo essere stato rilevato, scaricato e analizzato. Questa è nota come catena di richieste critiche e deve essere evitata.

Inoltre, il markup del rendering che utilizza JavaScript ha maggiori probabilità di generare attività lunghe rispetto a quello scaricato dal server in risposta a una richiesta di navigazione. Un uso intensivo del rendering lato client del codice HTML può influire negativamente sulla latenza di interazione. Ciò è particolarmente vero nei casi in cui il DOM di una pagina è molto grande, il che attiva un lavoro di rendering significativo quando JavaScript modifica il DOM.

Minimizzazione

Come per i CSS, la minimizzazione di JavaScript riduce le dimensioni del file di una risorsa script. Ciò può portare a download più rapidi, consentendo al browser di passare più rapidamente al processo di analisi e compilazione di JavaScript.

Inoltre, la minimizzazione di JavaScript va oltre la semplice minimizzazione di altri asset, come i CSS. Quando JavaScript viene minimizzato, non solo vengono rimossi spazi, tabulazioni e commenti, ma anche i simboli nel codice JavaScript di origine vengono abbreviati. Questa procedura è a volte nota come uglificazione. Per vedere la differenza, utilizza il seguente codice sorgente JavaScript:

// Unuglified JavaScript source code:
export function injectScript () {
  const scriptElement = document.createElement('script');
  scriptElement.src = '/js/scripts.js';
  scriptElement.type = 'module';

  document.body.appendChild(scriptElement);
}

Quando il codice sorgente JavaScript precedente viene uglificato, il risultato potrebbe avere un aspetto simile al seguente snippet di codice:

// Uglified JavaScript production code:
export function injectScript(){const t=document.createElement("script");t.src="/js/scripts.js",t.type="module",document.body.appendChild(t)}

Nello snippet precedente, puoi vedere che la variabile leggibile scriptElement nell'origine è stata abbreviata in t. Se applicato a una vasta raccolta di script, il risparmio può essere piuttosto significativo, senza influire sulle funzionalità fornite da JavaScript di produzione di un sito web.

Se usi un bundler per elaborare il codice sorgente del tuo sito web, spesso l'ottimizzazione viene eseguita automaticamente per le build di produzione. Anche gli elementi di uglificazione, come Terser, sono altamente configurabili, il che ti consente di modificare l'aggressività dell'algoritmo di uglificazione per ottenere il massimo risparmio. Tuttavia, le impostazioni predefinite per qualsiasi strumento di uglificazione sono in genere sufficienti per trovare il giusto equilibrio tra dimensioni dell'output e conservazione delle funzionalità.

Demo JavaScript

verifica le tue conoscenze

Qual è il modo migliore per caricare più file CSS nel browser?

La dichiarazione @import CSS.
Riprova.
Più elementi <link>.
risposta esatta.

Cosa fa il browser precarica lo scanner?

Si tratta di un parser HTML secondario che esamina il markup non elaborato per scoprire le risorse prima che l'analizzatore sintattico DOM possa rilevarle prima.
risposta esatta.
Rileva gli elementi <link rel="preload"> in una risorsa HTML.
Riprova.

Perché per impostazione predefinita il browser blocca temporaneamente l'analisi del codice HTML quando scarica le risorse JavaScript?

Per impedire un lampo di contenuti senza stile (FOUC).
Riprova.
Perché la valutazione di JavaScript è un'attività che richiede molta CPU e la messa in pausa dell'analisi HTML fornisce alla CPU maggiore larghezza di banda per completare il caricamento degli script.
Riprova.
Perché gli script possono modificare o accedere in altro modo al DOM.
risposta esatta.

A seguire: assistenza al browser con suggerimenti sulle risorse

Ora che conosci il modo in cui le risorse caricate nell'elemento <head> possono influire sul caricamento iniziale della pagina e su varie metriche, è il momento di andare avanti. Nel modulo successivo vengono esplorati i suggerimenti sulle risorse e il modo in cui possono fornire al browser preziosi suggerimenti per iniziare a caricare le risorse e aprire le connessioni ai server multiorigine prima di quanto potrebbe fare altrimenti il browser.