Prestazioni web semplificate - Google I/O 2018

In occasione della conferenza Google IO 2018, abbiamo presentato una raccolta di strumenti, librerie e tecniche di ottimizzazione che semplificano il miglioramento del rendimento del web. Qui li spieghiamo usando l'app The Oodles Theater. Parliamo anche dei nostri esperimenti con il caricamento predittivo e della nuova iniziativa Guess.js.

Addy Osmani
Addy Osmani
Ewa Gasperowicz

Nell'ultimo anno abbiamo lavorato duramente per capire come rendere il web più veloce e con un rendimento migliore. Questo ha portato a nuovi strumenti, approcci e librerie che vorremmo condividere con te in questo articolo. Nella prima parte, ti mostreremo alcune tecniche di ottimizzazione che abbiamo utilizzato nella pratica durante lo sviluppo dell'app Oodles Theater. Nella seconda parte parleremo dei nostri esperimenti con il caricamento predittivo e della nuova iniziativa Guess.js.

Necessità di prestazioni

Internet diventa sempre più pesante ogni anno. Se controlliamo lo stato del web, possiamo notare che una pagina mediana su dispositivi mobili ha un peso di circa 1,5 MB, principalmente JavaScript e immagini.

Le dimensioni crescenti dei siti web, insieme ad altri fattori, come la latenza della rete, le limitazioni della CPU, i pattern di blocco del rendering o il codice superfluo di terze parti, contribuiscono a complicare il problema del rendimento.

La maggior parte degli utenti valuta la velocità come il fattore più importante nella gerarchia della UX in base alle proprie esigenze. Non è troppo sorprendente, perché non puoi fare molto finché il caricamento di una pagina non è terminato. Non puoi ricavare valore dalla pagina né ammirarne l'estetica.

Gerarchia UX a piramide
Fig. 1. Quanto è importante la velocità per gli utenti? (Speed Matters, Vol. 3)

Sappiamo che il rendimento è importante per gli utenti, ma può anche sembrare un segreto scoprire da dove iniziare a ottimizzare. Fortunatamente, esistono strumenti che possono aiutarti lungo il percorso.

Lighthouse: una base per il flusso di lavoro delle prestazioni

Lighthouse fa parte di Chrome DevTools che ti consente di eseguire un controllo del tuo sito web e ti offre suggerimenti su come migliorarlo.

Di recente abbiamo lanciato una serie di nuovi controlli delle prestazioni molto utili nel flusso di lavoro di sviluppo quotidiano.

Nuovi controlli di Lighthouse
Fig. 2. Nuovi controlli di Lighthouse

Vediamo come puoi sfruttarli con un esempio pratico: l'app Oodles Theater. Si tratta di una piccola app web di dimostrazione, dove puoi provare alcuni dei nostri Doodle di Google interattivi preferiti e persino giocare a un paio di giochi.

Durante la creazione dell'app, volevamo assicurarci che avesse il rendimento migliore possibile. Il punto di partenza per l'ottimizzazione è stato un report Lighthouse.

Report Lighthouse per l'app Oodles
Fig. 3. Report Lighthouse per l'app Oodles

Le prestazioni iniziali della nostra app, osservate nel report Lighthouse, sono state piuttosto pessime. Su una rete 3G, l'utente doveva attendere 15 secondi per la prima visualizzazione significativa o per attivare l'interattività dell'app. Lighthouse ha evidenziato una serie di problemi con il nostro sito e il voto complessivo del rendimento di 23 rispecchia esattamente questo.

La pagina pesava circa 3,4 MB: dovevamo assolutamente tagliare qualcosa.

È iniziata così la nostra prima sfida in termini di rendimento: trovare elementi che possiamo rimuovere facilmente senza compromettere l'esperienza complessiva.

Opportunità di ottimizzazione delle prestazioni

Rimuovi le risorse non necessarie

Esistono alcuni elementi evidenti che possono essere rimossi in tutta sicurezza: spazi vuoti e commenti.

Vantaggi della minimizzazione
Fig. 4. Minimizza e comprimi JavaScript e CSS

Lighthouse mette in evidenza questa opportunità nel controllo CSS e JavaScript non minimizzati. Utilizzavamo webpack per la nostra procedura di compilazione, quindi per ottenere la minimizzazione abbiamo semplicemente utilizzato il plug-in Uglify JS.

La minimizzazione è un'attività comune, quindi dovresti essere in grado di trovare una soluzione pronta per qualsiasi processo di compilazione tu stia utilizzando.

Un altro controllo utile in questo spazio è Attiva la compressione del testo. Non c'è motivo di inviare file non compressi e la maggior parte delle CDN lo supporta out-of-the-box.

Utilizzavamo Firebase Hosting per ospitare il nostro codice e Firebase attiva il compressione gzip per impostazione predefinita, quindi, grazie all'hosting del nostro codice su una CDN ragionevole, abbiamo ottenuto questo vantaggio gratuitamente.

Mentre gzip è un modo molto popolare di compressione, anche altri meccanismi come Zopfli e Brotli stanno avendo successo. Brotli è supportato nella maggior parte dei browser e puoi utilizzare un programma binario per precomprimere gli asset prima di inviarli al server.

Utilizza criteri della cache efficaci

Il nostro passo successivo era evitare di inviare due volte risorse se non era necessario.

Il controllo Norme di cache inefficienti in Lighthouse ci ha aiutato a capire che potevamo ottimizzare le nostre strategie di memorizzazione nella cache per ottenere esattamente questo risultato. Impostando un'intestazione di scadenza max-age nel nostro server, ci siamo assicurati che, a seguito di una visita ripetuta, l'utente possa riutilizzare le risorse che ha scaricato in precedenza.

Idealmente, dovresti mirare a memorizzare nella cache il maggior numero possibile di risorse nel modo più sicuro possibile per il periodo di tempo più lungo possibile e fornire token di convalida per una convalida efficiente delle risorse aggiornate.

Rimuovi il codice inutilizzato

Finora abbiamo rimosso le parti evidenti del download non necessario, ma che dire di quelle meno evidenti? Ad esempio, codice inutilizzato.

Copertura codice in DevTools
Fig. 5. Controlla la copertura del codice

A volte includiamo nelle nostre app codice non strettamente necessario. Questo accade soprattutto se lavori alla tua app per un periodo di tempo più lungo, se il tuo team o le tue dipendenze cambiano e a volte una libreria orfana viene lasciata indietro. È esattamente quello che è successo a noi.

All'inizio utilizzavamo la libreria Material Components per realizzare rapidamente un prototipo della nostra app. Con il tempo siamo passati a un aspetto più personalizzato e abbiamo dimenticato completamente questa libreria. Fortunatamente, il controllo della copertura del codice ci ha aiutato a ritrovarlo nel nostro bundle.

Puoi controllare le statistiche sulla copertura del codice in DevTools, sia per il runtime sia per il tempo di caricamento della tua applicazione. Puoi vedere le due grandi strisce rosse nello screenshot in basso: oltre il 95% del CSS non era utilizzato, così come una grande quantità di JavaScript.

Lighthouse ha rilevato questo problema anche nel controllo delle regole CSS inutilizzate. Ha mostrato un potenziale risparmio di oltre 400 KB. Quindi siamo tornati al nostro codice e abbiamo rimosso sia la parte JavaScript sia la parte CSS della libreria.

Se rimuoviamo l'adattatore MVC, i nostri stili si riducono a 10 KB
Figura 6. Se eliminiamo l'adattatore MVC, i nostri stili si riducono a 10 KB.

In questo modo, il nostro bundle CSS è stato ridotto di 20 volte, un buon risultato per un commit di due righe.

Naturalmente, ciò ha fatto aumentare il nostro punteggio delle prestazioni, e anche il Tempo all'interattività è migliorato molto.

Tuttavia, con cambiamenti di questo tipo, non è sufficiente controllare solo le metriche e i punteggi. La rimozione del codice effettivo non è mai priva di rischi, quindi devi sempre fare attenzione a potenziali regressioni.

Il nostro codice non è stato utilizzato nel 95% dei casi, ma c'è ancora questo 5% da qualche parte. A quanto pare uno dei nostri componenti stava ancora utilizzando gli stili di quella libreria, ovvero le piccole frecce nel cursore del doodle. Tuttavia, poiché era così piccolo, abbiamo potuto incorporare manualmente questi stili nei pulsanti.

I pulsanti non funzionano a causa della raccolta mancante
Figura 7. Un componente utilizzava ancora la libreria rimossa

Pertanto, se rimuovi del codice, assicurati di avere implementato un flusso di lavoro di test adeguato per proteggerti da potenziali regressioni visive.

Evita payload di rete enormi

Sappiamo che le risorse di grandi dimensioni possono rallentare il caricamento delle pagine web. Possono costare denaro ai nostri utenti e avere un grande impatto sui loro piani dati, quindi è molto importante tenerne conto.

Lighthouse è stato in grado di rilevare un problema con alcuni dei nostri payload di rete utilizzando il controllo Payload di rete enorme.

Rileva payload di rete enormi
Fig. 8. Rileva payload di rete enormi

Abbiamo notato che il codice inviato era di oltre 3 MB, un volume piuttosto elevato, soprattutto sui dispositivi mobili.

Nella parte superiore di questo elenco, Lighthouse ha evidenziato che avevamo un bundle di fornitori JavaScript di 2 MB di codice non compresso. Questo è anche un problema evidenziato da webpack.

Come si suol dire, la richiesta più veloce è quella che non viene fatta.

Idealmente, dovresti misurare il valore di ogni singolo asset che offri agli utenti, misurarne le prestazioni e decidere se vale la pena caricarlo nell'esperienza iniziale. Perché a volte questi asset possono essere differiti, caricati in modo lazy o elaborati durante i tempi di inattività.

Nel nostro caso, poiché abbiamo a che fare con molti bundle JavaScript, abbiamo avuto la fortuna di avere a disposizione una vasta gamma di strumenti di controllo dei bundle JavaScript.

Controllo bundle JavaScript
Figura 9. Controllo dei bundle JavaScript

Abbiamo iniziato con webpack bundle analyzer, che ci ha comunicato che stavamo includendo una dipendenza chiamata unicode che occupava 1,6 MB di codice JavaScript analizzato, quindi molto.

Poi abbiamo aperto il nostro editor e, utilizzando il plug-in per l'importazione dei costi per il codice visivo, abbiamo potuto visualizzare il costo di ogni modulo che stavamo importando. In questo modo abbiamo scoperto quale componente includeva il codice che faceva riferimento a questo modulo.

Abbiamo quindi iniziato a utilizzare un altro strumento, BundlePhobia. Si tratta di uno strumento che ti consente di inserire il nome di qualsiasi pacchetto NPM e di visualizzare le dimensioni stimate in formato minimizzato e compresso con gzip. Abbiamo trovato una buona alternativa per il modulo slug utilizzato, che pesava solo 2,2 kB, quindi l'abbiamo spostata.

Questo ha avuto un grande impatto sul nostro rendimento. Grazie a questa modifica e alla scoperta di altre opportunità per ridurre le dimensioni del bundle JavaScript, abbiamo risparmiato 2,1 MB di codice.

Abbiamo registrato un miglioramento complessivo del 65%, tenendo conto delle dimensioni compresse e minimizzate di questi pacchetti. e abbiamo scoperto che valeva davvero la pena farlo.

In generale, quindi, cerca di eliminare i download non necessari nei tuoi siti e nelle tue app. Crea un inventario dei tuoi asset e misura il loro impatto sul rendimento per fare una differenza notevole, quindi assicurati di controllare gli asset con una certa regolarità.

Riduci il tempo di avvio di JavaScript con la suddivisione del codice

Anche se i payload di rete di grandi dimensioni possono avere un grande impatto sulla nostra app, c'è un'altra cosa che può avere un grande impatto, ovvero JavaScript.

JavaScript è l'asset più costoso. Se invii grandi bundle di JavaScript sui dispositivi mobili, può ritardare il momento in cui gli utenti sono in grado di interagire con i componenti dell'interfaccia utente. Ciò significa che possono toccare l'interfaccia utente senza che accada nulla di significativo. Per questo motivo, è importante capire perché JavaScript costa così tanto.

Questo è il modo in cui un browser elabora JavaScript.

Elaborazione di JavaScript
Fig. 10. Elaborazione di JavaScript

Innanzitutto dobbiamo scaricare lo script, abbiamo un motore JavaScript che deve analizzare il codice, compilarlo ed eseguirlo.

Queste fasi non richiedono molto tempo su un dispositivo di fascia alta come un computer o un laptop, o anche uno smartphone di fascia alta. Tuttavia, su uno smartphone medio questo processo può richiedere da cinque a dieci volte più tempo. Questo è ciò che ritarda l'interattività, quindi è importante provare a ridurlo.

Per aiutarti a scoprire questi problemi con la tua app, abbiamo introdotto un nuovo controllo del tempo di avvio di JavaScript in Lighthouse.

Tempo di avvio di JavaScript
Fig. 11. Controllo del tempo di avvio di JavaScript

Nel caso dell'app Oodle, ci è stato comunicato che il tempo impiegato per il caricamento di JavaScript è stato di 1,8 secondi. Il problema era che importavamo in modo statico tutti i nostri percorsi e componenti in un unico bundle JavaScript monolitico.

Una tecnica per aggirare questo problema è l'utilizzo della suddivisione del codice.

La suddivisione del codice è come la pizza

La suddivisione del codice è l'idea di non dare agli utenti una pizza intera di JavaScript, ma di dare loro una fetta alla volta in base alle loro esigenze.

La suddivisione del codice può essere applicata a livello di route o di componente. Funziona perfettamente con React e React Loadable, Vue.js, Angular, Polymer, Preact e molte altre librerie.

Abbiamo incorporato la suddivisione del codice nella nostra applicazione, siamo passati dalle importazioni statiche alle importazioni dinamiche, consentendoci di eseguire il caricamento lento in modo asincrono del codice in base alle necessità.

Suddivisione del codice con importazioni dinamiche
Fig. 13. Suddivisione del codice con importazioni dinamiche

Questo ha comportato una riduzione delle dimensioni dei bundle e del tempo di avvio di JavaScript. Il tempo è sceso a 0,78 secondi, rendendo l'app più veloce del 56%.

In generale, se stai creando un'esperienza con codice JavaScript, assicurati di inviare il codice solo all'utente di cui ha bisogno.

Sfrutta concetti come la suddivisione del codice, esplora idee come l'eliminazione degli elementi inutilizzati e dai un'occhiata al repo webpack-libs-optimizations per alcune idee su come ridurre le dimensioni della libreria se utilizzi webpack.

Ottimizza immagini

Barzelletta sulle prestazioni di caricamento delle immagini

Nell'app Oodle utilizziamo molte immagini. Purtroppo, Lighthouse era molto meno entusiasta di noi. Infatti, non abbiamo superato tutti e tre i controlli relativi alle immagini.

Abbiamo dimenticato di ottimizzare le nostre immagini, non le stavamo ridimensionando correttamente e potevamo anche ottenere un certo profitto dall'utilizzo di altri formati di immagine.

Controlli delle immagini
Fig. 14. Controlli delle immagini di Lighthouse

Abbiamo iniziato ottimizzando le nostre immagini.

Per un'ottimizzazione una tantum, puoi utilizzare strumenti visivi come ImageOptim o XNConvert.

Un approccio più automatizzato consiste nell'aggiungere un passaggio di ottimizzazione delle immagini al processo di compilazione, con librerie come imagemin.

In questo modo, le immagini aggiunte in futuro verranno ottimizzate automaticamente. Alcune CDN, ad esempio Akamai o soluzioni di terze parti come Cloudinary, Fastly o Uploadcare, offrono soluzioni complete per l'ottimizzazione delle immagini. Puoi anche semplicemente ospitare le tue immagini su questi servizi.

Se non vuoi farlo a causa del costo o di problemi di latenza, progetti come Thumbor o Imageflow offrono alternative self-hosted.

Prima e dopo l'ottimizzazione
Fig. 15. Prima e dopo l'ottimizzazione

Il nostro PNG di sfondo è stato segnalato in webpack come di grandi dimensioni, e giustamente. Dopo averlo ridimensionato correttamente in base all'area visibile e averlo eseguito tramite ImageOptim, siamo scesi a 100 KB, un valore accettabile.

Ripetere questo errore per più immagini sul nostro sito ci ha permesso di ridurre notevolmente il peso complessivo della pagina.

Utilizza il formato giusto per i contenuti animati

Le GIF possono essere molto costose. Sorprendentemente, il formato GIF non è mai stato concepito come piattaforma di animazione. Pertanto, il passaggio a un formato video più adatto consente grandi risparmi in termini di dimensioni dei file.

Nell'app Oodle, utilizzavamo una GIF come sequenza introduttiva nella home page. Secondo Lighthouse, potremmo risparmiare oltre 7 MB passando a un formato video più efficiente. Il nostro clip pesava circa 7,3 MB, troppo per qualsiasi sito web ragionevole, quindi lo abbiamo trasformato in un elemento video con due file di origine: uno in formato MP4 e uno in formato WebM per un supporto più ampio dei browser.

Sostituire le GIF animate con un video
Fig. 16. Sostituire le GIF animate con un video

Abbiamo utilizzato lo strumento FFmpeg per convertire la GIF animata in un file MP4. Il formato WebM offre risparmi ancora maggiori: l'API ImageOptim può eseguire questo tipo di conversione al posto tuo.

ffmpeg -i animation.gif -b:v 0 -crf 40 -vf scale=600:-1 video.mp4

Grazie a questa conversione siamo riusciti a risparmiare oltre l'80% del peso complessivo. In questo modo abbiamo ridotto il volume a circa 1 MB.

Tuttavia, 1 MB è una risorsa di grandi dimensioni da inviare, soprattutto per un utente con una larghezza di banda limitata. Fortunatamente, possiamo utilizzare l'API Effective Type per capire che la larghezza di banda è bassa e fornire un JPEG molto più piccolo.

Questa interfaccia utilizza il tempo di percorrenza effettivo e i valori di interruzione per stimare il tipo di rete utilizzato dall'utente. Restituisce semplicemente una stringa, 2G lento, 2G, 3G o 4G. Pertanto, a seconda di questo valore, se l'utente utilizza una rete inferiore al 4G, potremmo sostituire l'elemento video con l'immagine.

if (navigator.connection.effectiveType) { ... }

Elimina un po' l'esperienza, ma almeno il sito è utilizzabile con una connessione lenta.

Caricamento lento di immagini fuori schermo

Caroselli, cursori o pagine molto lunghe spesso caricano immagini, anche se l'utente non può vederle immediatamente sulla pagina.

Lighthouse segnala questo comportamento nell'audit delle immagini off-screen e puoi anche visualizzarlo nel riquadro della rete di DevTools. Se noti che vengono caricate molte immagini, ma solo alcune sono visibili nella pagina, potresti prendere in considerazione il caricamento differito.

Il caricamento lento non è ancora supportato in modo nativo nel browser, quindi dobbiamo utilizzare JavaScript per aggiungere questa funzionalità. Abbiamo utilizzato la libreria Lazysizes per aggiungere il comportamento di caricamento lazy alle nostre copertine Oodle.

<!-- Import library -->
import lazysizes from 'lazysizes'  <!-- or -->
<script src="lazysizes.min.js"></script>

<!-- Use it -->

<img data-src="image.jpg" class="lazyload"/>
<img class="lazyload"
    data-sizes="auto"
    data-src="image2.jpg"
    data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w"/>

Lazysizes è intelligente perché non solo monitora le modifiche alla visibilità dell'elemento, ma precompila anche in modo proattivo gli elementi vicini alla visualizzazione per un'esperienza utente ottimale. Offre inoltre un'integrazione facoltativa di IntersectionObserver, che consente ricerche di visibilità molto efficaci.

Dopo questa modifica, le nostre immagini vengono recuperate on demand. Per approfondire questo argomento, consulta images.guide, una risorsa molto utile e completa.

Aiuta il browser a fornire in anticipo le risorse fondamentali

Non tutti i byte inviati al browser hanno lo stesso grado di importanza e il browser lo sa. Molti browser adottano un'euristica per decidere cosa recuperare per primo. Pertanto, a volte recuperano il CSS prima delle immagini o degli script.

Un'idea che potrebbe essere utile è che noi, in qualità di autori della pagina, comunichiamo al browser ciò che è davvero importante per noi. Fortunatamente, negli ultimi due anni i fornitori di browser hanno aggiunto una serie di funzionalità per aiutarci, ad esempio i suggerimenti sulle risorse come link rel=preconnect, preload o prefetch.

Queste funzionalità introdotte nella piattaforma web aiutano il browser a recuperare l'elemento corretto al momento giusto e possono essere un po' più efficienti rispetto ad alcuni approcci basati su logica e caricamento personalizzato che vengono eseguiti utilizzando script.

Vediamo in che modo Lighthouse ci aiuta a utilizzare alcune di queste funzionalità in modo efficace.

La prima cosa che Lighthouse ci dice di fare è evitare più viaggi di andata e ritorno costosi verso qualsiasi origine.

Evita viaggi di andata e ritorno multipli e costosi verso qualsiasi origine
Fig. 17. Evitare più viaggi di andata e ritorno costosi verso qualsiasi origine

Nel caso dell'app Oodle, utilizziamo molto Google Fonts. Ogni volta che inserisci un stylesheet di Google Font nella pagina, verrà collegato a un massimo di due sottodomini. Lighthouse ci dice che, se riuscissimo a "riscaldare" la connessione, potremmo risparmiare fino a 300 millisecondi nel tempo di connessione iniziale.

Sfruttando la preconnessione di link rel, possiamo mascherare efficacemente la latenza della connessione.

Soprattutto con qualcosa come Google Fonts, dove il CSS dei nostri caratteri è ospitato su googleapis.com e le nostre risorse dei caratteri sono ospitate su Gstatic, questo può avere un impatto molto elevato. Abbiamo quindi applicato questa ottimizzazione e abbiamo risparmiato alcune centinaia di millisecondi.

Lighthouse suggerisce di precaricare le richieste chiave.

Precaricare le richieste di chiavi
Fig. 18. Precarica le richieste delle chiavi

<link rel=preload> è molto potente, informa il browser che è necessaria una risorsa nell'ambito della navigazione corrente e tenta di far sì che il browser la recuperi il prima possibile.

Qui Lighthouse ci dice che dovremmo precaricare le nostre risorse principali per i caratteri web, perché stiamo caricando in due caratteri web.

Il precaricamento di un carattere web ha questo aspetto: se specifichi rel=preload, passi in as con il tipo di carattere e poi specifichi il tipo di carattere che stai tentando di caricare, ad esempio woff2.

L'impatto che questo può avere sulla tua pagina è piuttosto netto.

Impatto del precaricamento delle risorse
Fig. 19. Impatto del precaricamento delle risorse

Normalmente, senza utilizzare il precaricamento dei link rel, se i caratteri web sono fondamentali per la tua pagina, il browser deve prima recuperare il codice HTML, analizzare il codice CSS e, molto più avanti, recuperare i caratteri web.

Con il precaricamento dei link rel, non appena il browser ha analizzato il codice HTML, può iniziare a recuperare i caratteri web molto prima. Nel caso della nostra app, è stato possibile risparmiare un secondo sul tempo impiegato per il rendering del testo utilizzando i nostri caratteri web.

Ora non è così semplice se vuoi provare a precaricare i caratteri utilizzando Google Fonts, c'è un problema.

Gli URL dei caratteri Google che specifichiamo nei nostri stili di carattere sono stati aggiornati abbastanza regolarmente dal team dei caratteri. Questi URL possono scadere o essere aggiornati con frequenza regolare, pertanto ti consigliamo di ospitare autonomamente i tuoi caratteri web se vuoi avere il controllo completo sull'esperienza di caricamento dei caratteri. Questo può essere un vantaggio perché ti consente di accedere ad elementi come il precaricamento dei link rel.

Nel nostro caso, abbiamo trovato lo strumento Google Web Fonts Helper molto utile per scaricare alcuni di questi caratteri web e configurarli localmente, quindi dai un'occhiata.

Che tu stia utilizzando caratteri web come parte delle risorse fondamentali o che si tratti di JavaScript, prova ad aiutare il browser a caricare le risorse fondamentali il prima possibile.

(Sperimentale) Suggerimenti di priorità

Abbiamo qualcosa di speciale da condividere con te oggi. Oltre a funzionalità come i suggerimenti delle risorse e il precaricamento, abbiamo lavorato a una nuovissima funzionalità sperimentale del browser che chiamiamo i suggerimenti prioritari.

Impostare la priorità per i contenuti inizialmente visibili
Fig. 20. Suggerimenti di priorità

Si tratta di una nuova funzionalità che consente di suggerire al browser l'importanza di una risorsa. Espone un nuovo attributo (importanza) con i valori low, high o auto.

In questo modo possiamo ridurre la priorità delle risorse meno importanti, come stili, immagini o chiamate API non critici, per ridurre le contese. Possiamo anche aumentare la priorità di elementi più importanti, come le nostre immagini hero.

Nel caso della nostra app Oodle, questo ci ha permesso di ottimizzare un aspetto pratico.

Impostare la priorità per i contenuti inizialmente visibili
Fig. 21. Impostare la priorità per i contenuti inizialmente visibili

Prima di aggiungere il caricamento differito alle nostre immagini, il browser caricava questo carosello di immagini con tutti i nostri scarabocchi e recuperava tutte le immagini all'inizio del carosello con una priorità elevata. Purtroppo, le immagini al centro del carosello erano le più importanti per l'utente. Abbiamo impostato l'importanza delle immagini di sfondo su molto bassa e quelle in primo piano su molto alta. Ciò ha avuto un impatto di due secondi sulla rete 3G lenta e sulla velocità con cui siamo riusciti a recuperare e visualizzare queste immagini. Quindi un'esperienza positiva.

Ci auguriamo di poter rendere disponibile questa funzionalità in Canary tra qualche settimana, quindi tieni d'occhio la situazione.

Avere una strategia di caricamento dei caratteri web

La tipografia è fondamentale per un design efficace e, se utilizzi caratteri web, idealmente non vuoi bloccare il rendering del testo e sicuramente non vuoi mostrare testo invisibile.

Ora lo evidenziamo in Lighthouse, con il controllo Evita il testo invisibile durante il caricamento dei caratteri web.

Evitare il testo invisibile durante il caricamento dei caratteri web
Fig. 22. Evita il testo invisibile durante il caricamento dei caratteri web.

Se carichi i tuoi caratteri web utilizzando un blocco di caratteri, consenti al browser di decidere cosa fare se il recupero di quel carattere web richiede molto tempo. Alcuni browser attenderanno fino a tre secondi prima di passare a un carattere di sistema e, una volta scaricato, lo sostituiranno con il carattere.

Stiamo cercando di evitare questo testo invisibile, quindi in questo caso non avremmo potuto vedere i doodle classici di questa settimana se il carattere web avesse impiegato troppo tempo. Fortunatamente, con una nuova funzionalità chiamata font-display, hai un controllo molto maggiore su questa procedura.

    @font-face {
      font-family: 'Montserrat';
      font-style: normal;
      font-display: swap;
      font-weight: 400;
      src: local('Montserrat Regular'), local('Montserrat-Regular'),
          /* Chrome 26+, Opera 23+, Firefox 39+ */
          url('montserrat-v12-latin-regular.woff2') format('woff2'),
            /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
          url('montserrat-v12-latin-regular.woff') format('woff');
    }

La visualizzazione dei caratteri ti consente di decidere come verranno visualizzati o come verrà eseguito il fallback dei caratteri web in base al tempo necessario per il loro scambio.

In questo caso utilizziamo lo scambio di visualizzazione dei caratteri. Scambia assegna al fronte un periodo di blocco di zero secondi e un periodo di scambio infinito. Ciò significa che il browser disegnerà il testo quasi immediatamente con un carattere di riserva se il caricamento del carattere richiede un po' di tempo. E lo sostituirà non appena il carattere sarà disponibile.

Nel caso della nostra app, è stato fantastico perché ci ha permesso di visualizzare un testo significativo molto presto e di passare al carattere web quando era pronto.

Risultato visualizzazione carattere
Fig. 23. Risultato della visualizzazione dei caratteri

In generale, se utilizzi i caratteri web, come fa una grande percentuale di siti web, adotta una buona strategia di caricamento dei caratteri web.

Esistono molte funzionalità della piattaforma web che puoi utilizzare per ottimizzare l'esperienza di caricamento dei caratteri, ma dai un'occhiata anche al repo Web Font Recipes di Zach Leatherman, perché è davvero fantastico.

Riduci gli script che bloccano la visualizzazione

Esistono altre parti della nostra applicazione che potremmo spingere prima nella catena di download per fornire almeno un'esperienza utente di base un po' prima.

Nella barra della sequenza temporale di Lighthouse puoi vedere che durante questi primi secondi, quando tutte le risorse vengono caricate, l'utente non può vedere alcun contenuto.

Ridurre le opportunità di stili CSS che bloccano il rendering
Fig. 24. Ridurre le opportunità di stili CSS che bloccano la visualizzazione

Il download e l'elaborazione di fogli di stile esterni impediscono il proseguimento del nostro processo di rendering.

Possiamo provare a ottimizzare il percorso di rendering critico caricando alcuni stili un po' prima.

Se estraiamo gli stili responsabili di questo rendering iniziale e li inseriamo in linea nel codice HTML, il browser è in grado di visualizzarli immediatamente senza attendere l'arrivo dei fogli di stile esterni.

Nel nostro caso, abbiamo utilizzato un modulo NPM denominato Critical per incorporare i contenuti critici in index.html durante un passaggio di compilazione.

Anche se questo modulo ha svolto la maggior parte del lavoro per noi, è stato comunque un po' complicato farlo funzionare senza problemi su percorsi diversi.

Se non fai attenzione o la struttura del tuo sito è molto complessa, potrebbe essere molto difficile inserire questo tipo di pattern se non hai pianificato l'architettura dell'app shell fin dall'inizio.

Ecco perché è così importante prendere in considerazione il rendimento fin dall'inizio. Se non pianifichi il rendimento fin dall'inizio, c'è un'alta probabilità di riscontrare problemi in seguito.

Alla fine il rischio è stato ripagato, siamo riusciti a farlo funzionare e l'app ha iniziato a pubblicare i contenuti molto prima, migliorando notevolmente il nostro primo tempo di visualizzazione significativo.

Il risultato

Questo è un lungo elenco di ottimizzazioni del rendimento che abbiamo applicato al nostro sito. Diamo un'occhiata al risultato. Questo è il modo in cui la nostra app è stata caricata su un dispositivo mobile di medie dimensioni su una rete 3G, prima e dopo l'ottimizzazione.

Il punteggio relativo alle prestazioni di Lighthouse è aumentato da 23 a 91. Ottimi progressi in termini di velocità. Tutte le modifiche sono state apportate grazie al controllo e al rispetto continuo del report Lighthouse. Se vuoi scoprire come abbiamo implementato tecnicamente tutti i miglioramenti, dai un'occhiata al nostro repository, in particolare alle PR che sono state caricate al suo interno.

Rendimento predittivo: esperienze utente basate sui dati

Riteniamo che il machine learning rappresenti un'opportunità entusiasmante per il futuro in molti settori. Un'idea che speriamo possa attivare altre sperimentazioni in futuro è che i dati reali possono davvero guidare le esperienze utente che stiamo creando.

Oggi prendiamo molte decisioni arbitrarie su ciò che l'utente potrebbe volere o di cui potrebbe avere bisogno e, di conseguenza, su cosa vale la pena prelevare, precaricare o memorizzare nella cache. Se indoviniamo, possiamo dare la priorità a una piccola quantità di risorse, ma è molto difficile scalare l'intero sito web.

Al momento abbiamo a disposizione dati per migliorare le nostre ottimizzazioni. Con l'API di reporting di Google Analytics, possiamo esaminare la pagina successiva più visitata e le percentuali di uscita per qualsiasi URL sul nostro sito e trarre conclusioni sulle risorse da dare la priorità.

Se combiniamo tutto questo con un modello di probabilità buono, evitiamo di sprecare i dati dei nostri utenti precaricando in modo aggressivo i contenuti. Possiamo sfruttare i dati di Google Analytics e utilizzare il machine learning e modelli come le catene di Markov o le reti neurali per implementare questi modelli.

Raggruppamento basato sui dati per le app web
Fig. 25. Raggruppamento basato sui dati per le app web

Per semplificare questi esperimenti, siamo lieti di annunciare una nuova iniziativa chiamata Guess.js.

Guess.js
Fig. 26. Guess.js

Guess.js è un progetto incentrato sulle esperienze utente basate sui dati per il web. Ci auguriamo che stimoli l'esplorazione dell'utilizzo dei dati per migliorare il rendimento del web e non solo. È tutto open source e disponibile su GitHub oggi. È stato creato in collaborazione con la community open source da Minko Gechev, Kyle Matthews di Gatsby, Katie Hempenius e altri.

Prova Guess.js e facci sapere cosa ne pensi.

Riepilogo

I punteggi e le metriche sono utili per migliorare la velocità del web, ma sono solo i mezzi, non gli obiettivi stessi.

Tutti abbiamo riscontrato caricamenti lenti delle pagine quando siamo in movimento, ma ora abbiamo l'opportunità di offrire ai nostri utenti esperienze più piacevoli che si caricano molto rapidamente.

Migliorare il rendimento è un percorso. Molti piccoli cambiamenti possono portare a grandi risultati. Utilizzando gli strumenti di ottimizzazione giusti e tenendo d'occhio i report Lighthouse, puoi offrire un'esperienza migliore e più inclusiva ai tuoi utenti.

Un ringraziamento speciale a: Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino, Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse e Google Doodles.