Refactoring CSS: ottimizzazione di dimensioni e prestazioni (parte 3)
Pubblicato: 2022-03-10Negli articoli precedenti di questa serie, abbiamo trattato il controllo dello stato della base di codice CSS e la strategia, il test e la manutenzione del refactoring CSS incrementale. Indipendentemente da quanto sia stata migliorata la base di codice CSS durante il processo di refactoring e quanto sia più manutenibile ed estensibile, il foglio di stile finale deve essere ottimizzato per le migliori prestazioni possibili e la minor dimensione possibile del file.
La distribuzione della base di codice refactoring non dovrebbe comportare prestazioni del sito Web peggiori e un'esperienza utente peggiore. Dopotutto, gli utenti non aspetteranno per sempre il caricamento del sito Web. Inoltre, il management sarà insoddisfatto della diminuzione del traffico e delle entrate causate dalla base di codice non ottimizzata, nonostante i miglioramenti della qualità del codice.
In questo articolo, tratteremo le strategie di ottimizzazione CSS che possono ottimizzare le dimensioni dei file CSS, i tempi di caricamento e le prestazioni di rendering. In questo modo, il codice CSS rifattorizzato non è solo più manutenibile ed estensibile, ma anche più performante e seleziona tutte le caselle importanti sia per l'utente finale che per il management.
Parte di: refactoring CSS
- Parte 1: Refactoring CSS: Introduzione
- Parte 2: Strategia CSS, test di regressione e manutenzione
- Parte 3: Ottimizzazione di dimensioni e prestazioni
- Iscriviti alla nostra newsletter via email per non perderti le prossime.
Ottimizzazione della dimensione del file del foglio di stile
L'ottimizzazione delle dimensioni del file si riduce alla rimozione di caratteri non necessari e alla formattazione e all'ottimizzazione del codice CSS per utilizzare sintassi o proprietà abbreviate diverse per ridurre il numero complessivo di caratteri in un file.
Ottimizzazione e minimizzazione
L'ottimizzazione e la minimizzazione dei CSS esistono da anni e sono diventate un punto fermo nell'ottimizzazione del frontend. Strumenti come cssnano e clean-css sono tra i miei strumenti preferiti quando si tratta di ottimizzazione e minimizzazione CSS. Offrono un'ampia varietà di opzioni di personalizzazione per controllare ulteriormente come il codice viene ottimizzato e quali browser sono supportati.
Questi strumenti funzionano in modo simile. Innanzitutto, il codice non ottimizzato viene analizzato e transpilato seguendo le regole impostate nel file config. Il risultato è il codice che utilizza meno caratteri ma mantiene comunque la formattazione (interruzioni di riga e spazi).
/* Before - original and unoptimized code */ .container { padding: 24px 16px 24px 16px; background: #222222; } /* After - optimized code with formatting */ .container { padding: 24px 16px; background: #222; }
Infine, il codice ottimizzato trasferito viene minimizzato rimuovendo tutta la formattazione del testo non necessaria . A seconda della base di codice e dei browser supportati impostati nella configurazione, è possibile rimuovere anche il codice con prefissi del fornitore deprecati.
/* Before - optimized code with formatting */ .container { padding: 24px 16px; background: #222; } /* After - optimized and minified code */ .container{padding:24px 16px;background:#222}
Anche in questo esempio di base, siamo riusciti a ridurre la dimensione complessiva del file da 76 byte a 55 byte, ottenendo una riduzione del 23%. A seconda della base di codice e degli strumenti di ottimizzazione e della configurazione, l'ottimizzazione e la minimizzazione CSS possono essere ancora più efficaci.
L'ottimizzazione e la minimizzazione dei CSS possono essere considerate una vittoria facile a causa del significativo vantaggio con poche modifiche al flusso di lavoro CSS. Ecco perché la minimizzazione dovrebbe essere trattata come l'ottimizzazione minima delle prestazioni e un requisito per tutti i fogli di stile del progetto.
Ottimizzazione delle query sui media
Quando scriviamo media query in CSS, specialmente quando si utilizzano più file (PostCSS o Sass), di solito non annidiamo il codice in una singola media query per un intero progetto. Per una migliore manutenibilità, modularità e struttura del codice, di solito scriviamo le stesse espressioni di query multimediali per più componenti CSS.
Consideriamo il seguente esempio di codice CSS non ottimizzato.
.page { display: grid; grid-gap: 16px; } @media (min-width: 768px) { .page { grid-template-columns: 268px auto; grid-gap: 24px; } } /* ... */ .products-grid { display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 16px; } @media (min-width: 768px) { .products-grid { grid-template-columns: repeat(3, 1fr); grid-gap: 20px; } }
Come puoi vedere, abbiamo un @media ripetuto @media (min-width: 768px)
per componente per una migliore leggibilità e manutenzione. Eseguiamo l'ottimizzazione e la minimizzazione su questo esempio di codice e vediamo cosa otteniamo.
.page{display:grid;grid-gap:16px}@media (min-width: 768px){.page{grid-template-columns:268px auto;grid-gap:24px}}.products-grid{display:grid;grid-template-columns:repeat(2,1fr);grid-gap:16px}@media (min-width: 768px){.products-grid{grid-template-columns:repeat(3,1fr);grid-gap:20px}}
Potrebbe essere un po' difficile da leggere, ma tutto ciò che dobbiamo notare è la query multimediale ripetuta @media (min-width: 768px)
. Abbiamo già concluso che vogliamo ridurre il numero di caratteri in un foglio di stile e possiamo annidare più selettori in una singola media query, quindi perché il minifier non ha rimosso l'espressione duplicata? C'è una semplice ragione per questo.
L'ordine delle regole è importante nei CSS, quindi per unire le query multimediali duplicate, è necessario spostare i blocchi di codice. Ciò comporterà la modifica degli ordini delle regole che possono causare effetti collaterali indesiderati negli stili.
Tuttavia, la combinazione di media query potrebbe potenzialmente ridurre le dimensioni del file, a seconda della base di codice e della struttura. Strumenti e pacchetti come postcss-sort-media-queries ci consentono di rimuovere le media query duplicate e di ridurre ulteriormente le dimensioni del file.
Naturalmente, c'è l'importante avvertimento di avere una struttura di base di codice CSS ben strutturata che non dipende dall'ordine delle regole. Questa ottimizzazione dovrebbe essere presa in considerazione quando si pianifica il refactor CSS e si stabiliscono le regole di base.
Consiglierei innanzitutto di verificare se il vantaggio di ottimizzazione supera i potenziali rischi. Questo può essere fatto facilmente eseguendo un audit CSS e controllando le statistiche delle query multimediali. In tal caso, consiglierei di aggiungerlo in un secondo momento ed eseguire test di regressione automatizzati per rilevare eventuali effetti collaterali e bug imprevisti che possono verificarsi di conseguenza.
Rimozione di CSS inutilizzati
Durante il processo di refactoring, c'è sempre la possibilità che ti ritroverai con alcuni stili legacy inutilizzati che non sono stati completamente rimossi o avrai alcuni stili aggiunti di recente che non vengono utilizzati. Questi stili si aggiungono anche al conteggio generale dei caratteri e alle dimensioni del file. Eliminare questi stili inutilizzati utilizzando strumenti automatizzati, tuttavia, può essere alquanto rischioso perché gli strumenti non possono prevedere con precisione quali stili vengono effettivamente utilizzati.
Strumenti come purgecss esaminano tutti i file nel progetto e utilizzano tutte le classi menzionate nei file come selettori, solo per peccare per eccesso di cautela e non eliminare accidentalmente i selettori per elementi dinamici iniettati da JavaScript, tra gli altri potenziali casi. Tuttavia, purgecss offre opzioni di configurazione flessibili come soluzioni alternative per questi potenziali problemi e rischi.
Tuttavia, questo miglioramento dovrebbe essere fatto solo quando i potenziali benefici superano i rischi . Inoltre, questa tecnica di ottimizzazione richiederà molto tempo per l'impostazione, la configurazione e il test e potrebbe causare problemi indesiderati in futuro, quindi procedi con cautela e assicurati che l'installazione sia a prova di proiettile.
Eliminazione dei CSS che bloccano il rendering
Per impostazione predefinita, CSS è una risorsa di blocco del rendering, il che significa che il sito Web non verrà visualizzato dall'utente fino a quando tutti i fogli di stile collegati e le relative dipendenze (font, ad esempio) non saranno stati scaricati e analizzati dal browser.
Se il file del foglio di stile ha una dimensione del file di grandi dimensioni o più dipendenze che si trovano su server o CDN di terze parti, il rendering del sito Web può subire un ritardo significativo a seconda della velocità e dell'affidabilità della rete.
Il Largest Contentful Paint (LCP) è diventato una metrica importante negli ultimi mesi. L'LCP non è importante solo per le prestazioni, ma anche per la SEO: i siti Web con punteggi LCP migliori avranno un posizionamento migliore nei risultati di ricerca. La rimozione di risorse che bloccano il rendering come CSS è un modo per migliorare il punteggio LCP.
Tuttavia, se potessimo rinviare il caricamento e l'elaborazione del foglio di stile, ciò comporterebbe Flash Of Unstyled Content (FOUC): il contenuto verrebbe visualizzato immediatamente all'utente e gli stili verrebbero caricati e applicati pochi istanti dopo. Questo interruttore potrebbe sembrare stridente e potrebbe persino confondere alcuni utenti.
CSS critico
Con Critical CSS, possiamo garantire che il sito Web venga caricato con la quantità minima di stili che è garantito per essere utilizzato nella pagina quando viene inizialmente visualizzata. In questo modo, possiamo rendere il FOUC molto meno evidente o addirittura eliminarlo nella maggior parte dei casi. Ad esempio, se la home page presenta un componente header con navigazione e un componente hero situato above-the-fold, ciò significa che il CSS critico conterrà tutti gli stili globali e dei componenti necessari per questi componenti, mentre gli stili per altri componenti nella pagina sarà differito.
Questo CSS è integrato in HTML sotto un tag di style
, quindi gli stili vengono caricati e analizzati insieme al file HTML. Sebbene ciò comporterà una dimensione del file HTML leggermente maggiore (che dovrebbe anche essere ridotta a icona), tutti gli altri CSS non critici verranno posticipati e non verranno caricati immediatamente e il sito Web verrà visualizzato più velocemente. Tutto sommato, i vantaggi superano l'aumento delle dimensioni del file HTML.
<head> <style type="text/css"><!-- Minified Critical CSS markup --></style> </head>
Esistono molti strumenti automatizzati e pacchetti NPM, a seconda della configurazione, in grado di estrarre CSS critici e generare fogli di stile differiti.
Rinviare i fogli di stile
Come facciamo esattamente a rendere il CSS non bloccante? Sappiamo che non dovrebbe essere referenziato nell'elemento head
HTML quando la pagina HTML viene scaricata per la prima volta. Demian Renzulli ha delineato questo metodo nel suo articolo.
Non esiste un approccio HTML nativo (per il momento) per ottimizzare o rinviare il caricamento delle risorse che bloccano il rendering, quindi è necessario utilizzare JavaScript per inserire il foglio di stile non critico nel markup HTML dopo il rendering iniziale. Dobbiamo anche assicurarci che questi stili vengano caricati in modo non ottimale (blocco del rendering) se un utente sta visitando la pagina con JavaScript non abilitato nel browser.
<!-- Deferred stylesheet --> <link rel="preload" as="style" href="path/to/stylesheet.css" onload="this.onload=null;this.rel='stylesheet'"> <!-- Fallback --> <noscript> <link rel="stylesheet" href="path/to/stylesheet.css"> </noscript>
Con link rel="preload" as="style"
assicura che il file del foglio di stile venga richiesto in modo asincrono, mentre il gestore JavaScript onload
si assicura che il file venga caricato ed elaborato dal browser al termine del caricamento del documento HTML. Sono necessarie alcune operazioni di pulizia, quindi è necessario impostare l' onload
su null
per evitare che questa funzione venga eseguita più volte e causi re-render non necessari.
Questo è esattamente il modo in cui Smashing Magazine gestisce i suoi fogli di stile. Ogni modello (homepage, categorie di articoli, pagine di articoli, ecc.) ha un CSS critico specifico del modello integrato all'interno del tag di style
HTML nell'elemento head
e un foglio di stile main.css
differito che contiene tutti gli stili non critici.
Tuttavia, invece di attivare il parametro rel
, qui possiamo vedere la media query passare dal supporto di print
a bassa priorità automaticamente rinviato all'attributo ad alta priorità all
quando la pagina ha terminato il caricamento. Questo è un approccio alternativo, ugualmente valido per posticipare il caricamento di fogli di stile non critici.
<link href="/css/main.css" media="print" onload="this.media='all'" rel="stylesheet">
Divisione e caricamento condizionale di fogli di stile con media query
Per i casi in cui il file del foglio di stile finale ha una dimensione del file grande anche dopo l'applicazione delle suddette ottimizzazioni, è possibile dividere i fogli di stile in più file in base alle query multimediali e utilizzare la proprietà media sui fogli di stile a cui si fa riferimento nell'elemento HTML del collegamento per caricarli in modo condizionale .
<link href="print.css" rel="stylesheet" media="print"> <link href="mobile.css" rel="stylesheet" media="all"> <link href="tablet.css" rel="stylesheet" media="screen and (min-width: 768px)"> <link href="desktop.css" rel="stylesheet" media="screen and (min-width: 1366px)">
In questo modo, se viene utilizzato un approccio mobile-first, gli stili per schermi di dimensioni maggiori non verranno scaricati o analizzati su dispositivi mobili che potrebbero essere eseguiti su reti più lente o inaffidabili.
Tanto per ribadire, questo metodo dovrebbe essere utilizzato se il risultato dei metodi di ottimizzazione precedentemente menzionati risulta in un foglio di stile con dimensioni del file non ottimali. Nei casi normali, questo metodo di ottimizzazione non sarà così efficace o di impatto, a seconda delle dimensioni del singolo foglio di stile.
Rinviare file di font e fogli di stile
Anche il differimento dei fogli di stile dei caratteri (file di Google Font, ad esempio) potrebbe essere vantaggioso per le prestazioni di rendering iniziali. Abbiamo concluso che i fogli di stile bloccano il rendering, ma lo sono anche i file di font a cui si fa riferimento nel foglio di stile. I file dei caratteri aggiungono anche un po' di sovraccarico alle prestazioni di rendering iniziali.
Il caricamento di fogli di stile e file di font è un argomento complesso e approfondirlo richiederebbe un articolo completamente nuovo solo per spiegare tutti gli approcci praticabili. Fortunatamente, Zach Leatherman ha delineato molte strategie praticabili in questa fantastica guida completa e ha riassunto i pro e i contro di ciascun approccio. Se utilizzi Google Fonts, Harry Roberts ha delineato una strategia per il caricamento più veloce di Google Fonts.
Se decidi di rinviare i fogli di stile dei caratteri, ti ritroverai con Flash of Unstyled Text (FOUT). La pagina verrà inizialmente visualizzata con il font di riserva fino a quando i file dei font e i fogli di stile differiti non saranno stati scaricati e analizzati, a quel punto verranno applicati i nuovi stili. Questa modifica può essere molto evidente e può causare cambiamenti di layout e confondere gli utenti, a seconda del singolo caso.
Barry Pollard ha delineato alcune strategie che possono aiutarci a gestire FOUT e ha parlato della prossima funzione CSS di regolazione delle dimensioni che fornirà un modo più semplice e nativo di gestire FOUT.
Ottimizzazioni lato server
Compressione HTTP
Oltre alla minimizzazione e all'ottimizzazione delle dimensioni dei file, è possibile utilizzare gli algoritmi di compressione HTTP come Gzip e Brotli per ridurre ulteriormente le dimensioni del file scaricato.
La compressione HTTP deve essere configurata sul server che dipende dallo stack tecnico e dalla configurazione. Tuttavia, i vantaggi in termini di prestazioni possono variare e potrebbero non avere lo stesso impatto della minimizzazione e ottimizzazione dei fogli di stile standard, poiché i browser decomprimeranno comunque i file compressi e dovranno analizzarli.
Fogli di stile nella cache
La memorizzazione nella cache di file statici è un'utile strategia di ottimizzazione. I browser dovranno comunque scaricare i file statici dal server al primo caricamento, ma una volta inseriti nella cache verranno caricati da esso direttamente nelle richieste successive, velocizzando il processo di caricamento.
La memorizzazione nella cache può essere controllata tramite l'intestazione HTTP Cache-Control a livello di server (ad esempio, utilizzando il file .htaccess
su un server Apache).
Con max-age
possiamo indicare per quanto tempo il file deve rimanere nella cache (in secondi) nel browser e con public
, stiamo indicando che il file può essere memorizzato nella cache dal browser e da qualsiasi altra cache.
Cache-Control: public, max-age=604800
È possibile ottenere una strategia cache più aggressiva ed efficace per le risorse statiche con la configurazione immutable
. Questo dice al browser che questo particolare file non cambierà mai e che qualsiasi nuovo aggiornamento comporterà l'eliminazione di questo file e un nuovo file con un nome file diverso prenderà il suo posto. Questo è noto come busting della cache .
Cache-Control: public, max-age=604800, immutable
Senza un'adeguata strategia di busting della cache , c'è il rischio di perdere il controllo sui file che vengono memorizzati nella cache del browser dell'utente. Ciò significa che se il file dovesse cambiare, il browser non sarà in grado di sapere che dovrebbe scaricare il file aggiornato e non utilizzare il file memorizzato nella cache obsoleto. E da quel momento in poi, non c'è praticamente nulla che possiamo fare per risolverlo e l'utente rimarrà bloccato con il file obsoleto fino alla scadenza.
Per i fogli di stile, ciò potrebbe significare che se dovessimo aggiornare i file HTML con nuovi contenuti e componenti che richiedono un nuovo stile, questi stili non verranno visualizzati perché il foglio di stile obsoleto viene memorizzato nella cache senza una strategia di busting della cache e il browser non lo saprà deve scaricare il nuovo file.
Prima di utilizzare una strategia di memorizzazione nella cache per fogli di stile o altri file statici, è necessario implementare meccanismi efficaci di busting della cache per evitare che file statici obsoleti rimangano bloccati nella cache dell'utente. È possibile utilizzare uno dei seguenti meccanismi di controllo delle versioni per il busting della cache:
- Aggiunta di una stringa di query al nome del file.
Ad esempiostyles.css?v=1.0.1.
Tuttavia, alcune CDN possono ignorare o rimuovere completamente la stringa di query dal nome del file e far sì che il file rimanga bloccato nella cache dell'utente e non si aggiorni mai. - Modifica del nome del file o aggiunta di un hash.
Ad esempiostyles.a1bc2.css
ostyles.v1.0.1.css.
Questo è più affidabile ed efficace rispetto all'aggiunta di una stringa di query al nome del file.
CDN o self-hosting?
Content Delivery Network (CDN) è un gruppo di server distribuiti geograficamente che vengono comunemente utilizzati per la consegna affidabile e veloce di risorse statiche come immagini, video, file HTML, file CSS, file JavaScript, ecc.
Sebbene le CDN possano sembrare un'ottima alternativa alle risorse statiche di self-hosting, Harry Roberts ha condotto ricerche approfondite sull'argomento e ha concluso che le risorse di self-hosting sono più vantaggiose per le prestazioni.
“Ci sono davvero poche ragioni per lasciare le tue risorse statiche sull'infrastruttura di qualcun altro. I vantaggi percepiti sono spesso un mito e, anche se non lo fossero, i compromessi semplicemente non ne valgono la pena. Il caricamento di risorse da origini multiple è dimostrabilmente più lento".
Detto questo, consiglierei di ospitare automaticamente i fogli di stile (fogli di stile dei caratteri inclusi, se possibile) per impostazione predefinita e di passare alla CDN solo se ci sono ragioni valide o altri vantaggi per farlo.
Controllo delle dimensioni e delle prestazioni dei file CSS
WebPageTest e altri strumenti di controllo delle prestazioni simili possono essere utilizzati per ottenere una panoramica dettagliata del processo di caricamento del sito Web, delle dimensioni dei file, delle risorse di blocco del rendering, ecc. Questi strumenti possono darti un'idea di come viene caricato il tuo sito Web su un'ampia gamma di dispositivi — da un PC desktop in esecuzione su una rete ad alta velocità a smartphone di fascia bassa in esecuzione su reti lente e inaffidabili.
Eseguiamo un controllo delle prestazioni su un sito Web menzionato nel primo articolo di questa serie, quello con i 2 MB di CSS ridotti.
In primo luogo, daremo un'occhiata alla suddivisione del contenuto per determinare quali risorse occupano più larghezza di banda. Dai grafici seguenti, possiamo vedere che le immagini assorbono la maggior parte delle richieste, il che significa che devono essere caricate in modo lento. Dal secondo grafico, possiamo vedere che i fogli di stile e i file JavaScript sono i più grandi in termini di dimensioni del file. Questa è una buona indicazione del fatto che questi file devono essere minimizzati e ottimizzati, refactoring o suddivisi in più file e caricati in modo asincrono.
Possiamo trarre ancora più conclusioni dai grafici di Web Vitals. Dando un'occhiata al grafico LCP (The Largest Contentful Paint), possiamo ottenere una panoramica dettagliata delle risorse che bloccano il rendering e di quanto influiscono sul rendering iniziale.
Potremmo già concludere che il foglio di stile del sito Web avrà il maggiore impatto sull'LCP e sulle statistiche di caricamento. Tuttavia, possiamo vedere fogli di stile dei caratteri, file JavaScript e immagini referenziate all'interno dei fogli di stile che bloccano anche il rendering. Sapendo che possiamo applicare i suddetti metodi di ottimizzazione per ridurre il tempo LCP eliminando le risorse che bloccano il rendering.
Conclusione
Il processo di refactoring non è completo quando l'integrità e la qualità del codice sono state migliorate e quando i punti deboli e i problemi della base di codice sono stati risolti. La base di codice rifattorizzato dovrebbe comportare prestazioni identiche o migliorate rispetto alla base di codice legacy.
Gli utenti finali non dovrebbero riscontrare problemi di prestazioni o lunghi tempi di caricamento dalla base di codice sottoposta a refactoring. Fortunatamente, esistono molti metodi per assicurarsi che le basi di codice siano robuste e performanti, dai semplici metodi di minimizzazione e ottimizzazione ai metodi più complessi come l' eliminazione delle risorse di blocco del rendering e la suddivisione del codice.
Possiamo utilizzare vari strumenti di controllo delle prestazioni come WebPageTest per ottenere una panoramica dettagliata dei tempi di caricamento, delle prestazioni, delle risorse di blocco del rendering e di altri fattori in modo da poter affrontare questi problemi in modo tempestivo ed efficace.
Parte di: refactoring CSS
- Parte 1: Refactoring CSS: Introduzione
- Parte 2: Refactoring CSS: strategia, test di regressione e manutenzione
- Parte 3: Refactoring CSS: ottimizzazione di dimensioni e prestazioni
- Iscriviti alla nostra newsletter via email per non perderti le prossime.
Riferimenti
- "CSS blocco rendering", Ilya Grigorik
- "Rimanda CSS non critici", Demian Renzulli
- "Una guida completa alle strategie di caricamento dei caratteri", Zach Leatherman
- "Un nuovo modo per ridurre l'impatto del caricamento dei caratteri: descrittori di caratteri CSS", Barry Pollard
- "Ospita autonomamente le tue risorse statiche", Harry Roberts
- "Ottimizza il caricamento e il rendering di WebFont", Ilya Grigorik