Performance front-end 2021: ottimizzazione della consegna

Pubblicato: 2022-03-10
Riassunto veloce ↬ Facciamo il 2021… veloce! Un elenco di controllo annuale delle prestazioni del front-end con tutto ciò che devi sapere per creare esperienze veloci sul Web oggi, dalle metriche agli strumenti e alle tecniche di front-end. Aggiornato dal 2016.

Sommario

  1. Prepararsi: pianificazione e metriche
  2. Stabilire obiettivi realistici
  3. Definire l'ambiente
  4. Ottimizzazioni degli asset
  5. Costruisci ottimizzazioni
  6. Ottimizzazioni di consegna
  7. Rete, HTTP/2, HTTP/3
  8. Test e monitoraggio
  9. Vittorie veloci
  10. Tutto in una pagina
  11. Scarica la lista di controllo (PDF, Apple Pages, MS Word)
  12. Iscriviti alla nostra newsletter via email per non perdere le prossime guide.

Ottimizzazioni di consegna

  1. Usiamo il defer per caricare JavaScript critico in modo asincrono?
    Quando l'utente richiede una pagina, il browser recupera l'HTML e costruisce il DOM, quindi recupera il CSS e costruisce il CSSOM, quindi genera un albero di rendering abbinando il DOM e il CSSOM. Se è necessario risolvere qualsiasi JavaScript, il browser non avvierà il rendering della pagina fino a quando non verrà risolto, ritardando così il rendering. Come sviluppatori, dobbiamo dire esplicitamente al browser di non aspettare e di iniziare a eseguire il rendering della pagina. Il modo per farlo per gli script è con gli attributi defer e async in HTML.

    In pratica, risulta che è meglio usare defer invece di async . Ah, qual è la differenza di nuovo? Secondo Steve Souders, una volta che gli script async arrivano, vengono eseguiti immediatamente, non appena lo script è pronto. Se ciò accade molto velocemente, ad esempio quando lo script è già nella cache, può effettivamente bloccare il parser HTML. Con defer , il browser non esegue gli script finché l'HTML non viene analizzato. Quindi, a meno che tu non abbia bisogno di JavaScript da eseguire prima di avviare il rendering, è meglio usare defer . Inoltre, più file asincroni verranno eseguiti in un ordine non deterministico.

    Vale la pena notare che ci sono alcune idee sbagliate su async e defer . Ancora più importante, async non significa che il codice verrà eseguito ogni volta che lo script è pronto; significa che verrà eseguito ogni volta che gli script sono pronti e tutto il lavoro di sincronizzazione precedente è terminato. Nelle parole di Harry Roberts, "Se inserisci uno script async dopo gli script di sincronizzazione, il tuo script async è veloce quanto lo script di sincronizzazione più lento".

    Inoltre, non è consigliabile utilizzare sia async che defer . I browser moderni supportano entrambi, ma ogni volta che vengono utilizzati entrambi gli attributi, l' async vincerà sempre.

    Se desideri approfondire i dettagli, Milica Mihajlija ha scritto una guida molto dettagliata su Costruire il DOM più velocemente, entrando nei dettagli dell'analisi speculativa, dell'asincrono e del differimento.

  2. Carica pigramente componenti costosi con IntersectionObserver e suggerimenti per la priorità.
    In generale, si consiglia di caricare in modo pigro tutti i componenti costosi, come JavaScript pesante, video, iframe, widget e potenzialmente immagini. Il caricamento lento nativo è già disponibile per immagini e iframe con l'attributo di loading (solo Chromium). Sotto il cofano, questo attributo rinvia il caricamento della risorsa fino a quando non raggiunge una distanza calcolata dal viewport.
    <!-- Lazy loading for images, iframes, scripts. Probably for images outside of the viewport. --> <img loading="lazy" ... /> <iframe loading="lazy" ... /> <!-- Prompt an early download of an asset. For critical images, eg hero images. --> <img loading="eager" ... /> <iframe loading="eager" ... />

    Tale soglia dipende da alcune cose, dal tipo di risorsa immagine che viene recuperata al tipo di connessione efficace. Ma gli esperimenti condotti utilizzando Chrome su Android suggeriscono che su 4G, il 97,5% delle immagini under-the-fold caricate in modo pigro sono state completamente caricate entro 10 ms da quando sono diventate visibili, quindi dovrebbe essere sicuro.

    Possiamo anche utilizzare l'attributo di importance ( high o low ) su un elemento <script> , <img> o <link> (solo Blink). In effetti, è un ottimo modo per ridurre la priorità alle immagini nei caroselli, nonché per ridefinire la priorità degli script. Tuttavia, a volte potremmo aver bisogno di un controllo un po' più granulare.

    <!-- When the browser assigns "High" priority to an image, but we don't actually want that. --> <img src="less-important-image.svg" importance="low" ... /> <!-- We want to initiate an early fetch for a resource, but also deprioritize it. --> <link rel="preload" importance="low" href="/script.js" as="script" />

    Il modo più efficace per eseguire un caricamento lento leggermente più sofisticato consiste nell'usare l'API Intersection Observer che fornisce un modo per osservare in modo asincrono le modifiche nell'intersezione di un elemento di destinazione con un elemento predecessore o con il viewport di un documento di primo livello. Fondamentalmente, devi creare un nuovo oggetto IntersectionObserver , che riceve una funzione di callback e un insieme di opzioni. Quindi aggiungiamo un obiettivo da osservare.

    La funzione di callback viene eseguita quando il target diventa visibile o invisibile, quindi quando intercetta il viewport, puoi iniziare a intraprendere alcune azioni prima che l'elemento diventi visibile. In effetti, abbiamo un controllo granulare su quando deve essere invocata la richiamata dell'osservatore, con rootMargin (margine attorno alla radice) e threshold (un singolo numero o una matrice di numeri che indicano a quale percentuale della visibilità del bersaglio stiamo puntando).

    Alejandro Garcia Anglada ha pubblicato un pratico tutorial su come implementarlo effettivamente, Rahul Nanwani ha scritto un post dettagliato sul caricamento lento delle immagini in primo piano e di sfondo e Google Fundamentals fornisce anche un tutorial dettagliato sul caricamento lento di immagini e video con Intersection Observer.

    Ricordi lunghe letture di narrazione artistica con oggetti in movimento e appiccicosi? Puoi anche implementare lo scrollytelling performante con Intersection Observer.

    Controlla di nuovo cos'altro potresti caricare pigro. Anche le stringhe di traduzione e le emoji a caricamento lento potrebbero aiutare. In questo modo, Mobile Twitter è riuscito a ottenere un'esecuzione JavaScript più veloce dell'80% dalla nuova pipeline di internazionalizzazione.

    Una breve parola di cautela però: vale la pena notare che il caricamento lento dovrebbe essere un'eccezione piuttosto che la regola. Probabilmente non è ragionevole caricare in modo pigro tutto ciò che si desidera che le persone vedano rapidamente, ad esempio immagini della pagina del prodotto, immagini dell'eroe o uno script necessario affinché la navigazione principale diventi interattiva.

Un esempio che mostra una vecchia soglia di 3000 px con 160 KB di download (a sinistra) mentre la nuova soglia ha una quantità di 1250 px con solo 90 KB di download (a destra) che mostra un miglioramento del risparmio di dati pigri nel caricamento di img
Sulle connessioni veloci (es. 4G), le soglie di distanza dalla finestra di visualizzazione di Chrome sono state recentemente ridotte da 3000px a 1250px, e sulle connessioni più lente (es. 3G), la soglia è cambiata da 4000px a 2500px. (Grande anteprima)
Un'illustrazione con testo attorno a un telefono cellulare con l'interfaccia utente di Twitter mostrata, che spiega i miglioramenti degli strumenti da stringhe di traduzione a carico lento
Con il caricamento lento delle stringhe di traduzione, Mobile Twitter è riuscito a ottenere un'esecuzione JavaScript più veloce dell'80% dalla nuova pipeline di internazionalizzazione. (Credito immagine: Addy Osmani) (Anteprima grande)
  1. Carica le immagini progressivamente.
    Potresti persino portare il caricamento lento al livello successivo aggiungendo il caricamento progressivo delle immagini alle tue pagine. Analogamente a Facebook, Pinterest, Medium e Wolt, è possibile caricare prima immagini di bassa qualità o addirittura sfocate, quindi, mentre la pagina continua a caricarsi, sostituirle con le versioni di qualità completa utilizzando la tecnica BlurHash o LQIP (Low Quality Image Placeholders) tecnica.

    Le opinioni divergono se queste tecniche migliorano o meno l'esperienza dell'utente, ma migliorano decisamente il tempo di First Contentful Paint. Possiamo persino automatizzarlo utilizzando SQIP che crea una versione di bassa qualità di un'immagine come segnaposto SVG o segnaposto immagine sfumata con gradienti lineari CSS.

    Questi segnaposto potrebbero essere incorporati all'interno di HTML poiché si comprimono naturalmente bene con i metodi di compressione del testo. Nel suo articolo, Dean Hume ha descritto come questa tecnica può essere implementata utilizzando Intersection Observer.

    Ricaderci? Se il browser non supporta l'osservatore di intersezione, possiamo comunque caricare in modo lento un polyfill o caricare immediatamente le immagini. E c'è anche una libreria per questo.

    Vuoi diventare più elegante? È possibile tracciare le immagini e utilizzare forme e bordi primitivi per creare un segnaposto SVG leggero, caricarlo prima e quindi passare dall'immagine vettoriale segnaposto all'immagine bitmap (caricata).

  2. Tre diverse versioni che mostrano la tecnica di caricamento pigro SVG di Jose M. Perez, una versione simile all'arte del cubismo a sinistra, una versione sfocata pixelata al centro e un'immagine corretta dello stesso Jose a destra
    Tecnica di caricamento pigro SVG di Jose M. Perez. (Grande anteprima)
  3. Rimandi il rendering con content-visibility ?
    Per layout complessi con molti blocchi di contenuto, immagini e video, la decodifica dei dati e il rendering dei pixel potrebbero essere un'operazione piuttosto costosa, specialmente su dispositivi di fascia bassa. Con content-visibility: auto , possiamo richiedere al browser di saltare il layout dei bambini mentre il contenitore è al di fuori del viewport.

    Ad esempio, potresti saltare il rendering del piè di pagina e delle sezioni finali sul caricamento iniziale:

    footer { content-visibility: auto; contain-intrinsic-size: 1000px; /* 1000px is an estimated height for sections that are not rendered yet. */ }

    Si noti che la visibilità del contenuto: auto; si comporta come overflow: nascosto; , ma puoi risolverlo applicando padding-left e padding-right invece del margin-left: auto; , margin-right: auto; e una larghezza dichiarata. Il riempimento sostanzialmente consente agli elementi di traboccare nella casella del contenuto ed entrare nella casella di riempimento senza lasciare il modello della scatola nel suo insieme e essere tagliato.

    Inoltre, tieni presente che potresti introdurre alcuni CLS quando alla fine viene eseguito il rendering di nuovi contenuti, quindi è una buona idea usare contain-intrinsic-size con un segnaposto di dimensioni adeguate ( grazie, Una! ).

    Thijs Terluin ha molti più dettagli su entrambe le proprietà e su come viene calcolata la contain-intrinsic-size dal browser, Malte Ubl mostra come calcolarla e un breve video esplicativo di Jake e Surma spiega come funziona.

    E se hai bisogno di diventare un po' più granulare, con CSS Containment, puoi saltare manualmente il layout, lo stile e il lavoro di disegno per i discendenti di un nodo DOM se hai bisogno solo di dimensioni, allineamento o stili calcolati su altri elementi — o l'elemento è attualmente fuori tela.

Le prestazioni di rendering al carico iniziale sono 2.288 ms per la linea di base (a sinistra) e 13.464 ms per i blocchi con visibilità del contenuto: auto (a destra)
Nella demo, l'applicazione content-visibility: auto alle aree di contenuto in blocchi offre un aumento delle prestazioni di rendering di 7 volte al carico iniziale. (Grande anteprima)
  1. Rinvii la decodifica con decoding="async" ?
    A volte il contenuto appare fuori schermo, ma vogliamo assicurarci che sia disponibile quando i clienti ne hanno bisogno, idealmente, non bloccando nulla nel percorso critico, ma decodificando e visualizzando in modo asincrono. Possiamo usare decoding="async" per dare al browser il permesso di decodificare l'immagine fuori dal thread principale, evitando l'impatto dell'utente del tempo CPU utilizzato per decodificare l'immagine (tramite Malte Ubl):

    <img decoding="async" … />

    In alternativa, per le immagini fuori schermo, possiamo visualizzare prima un segnaposto e, quando l'immagine è all'interno del viewport, utilizzando IntersectionObserver, attivare una chiamata di rete per scaricare l'immagine in background. Inoltre, possiamo rinviare il rendering fino alla decodifica con img.decode() o scaricare l'immagine se l'API Image Decode non è disponibile.

    Durante il rendering dell'immagine, ad esempio, possiamo utilizzare animazioni di dissolvenza in entrata. Katie Hempenius e Addy Osmani condividono ulteriori approfondimenti nel loro discorso Speed ​​at Scale: Web Performance Tips and Tricks from the Trenches.

  2. Generate e servite CSS critici?
    Per garantire che i browser inizino a visualizzare la tua pagina il più rapidamente possibile, è diventata una pratica comune raccogliere tutti i CSS necessari per iniziare a eseguire il rendering della prima parte visibile della pagina (nota come "CSS critico" o "CSS above-the-fold ") e includerlo in linea nel <head> della pagina, riducendo così i roundtrip. A causa delle dimensioni limitate dei pacchetti scambiati durante la fase di avvio lento, il tuo budget per CSS critici è di circa 14 KB.

    Se vai oltre, il browser avrà bisogno di ulteriori roundtrip per recuperare più stili. CriticalCSS e Critical ti consentono di generare CSS critici per ogni modello che stai utilizzando. Nella nostra esperienza, tuttavia, nessun sistema automatico è mai stato migliore della raccolta manuale di CSS critici per ogni modello, e in effetti questo è l'approccio a cui siamo tornati di recente.

    È quindi possibile incorporare CSS critici e caricare in modo pigro il resto con il plug-in Critters Webpack. Se possibile, prendi in considerazione l'utilizzo dell'approccio inline condizionale utilizzato dal Filament Group o converti al volo il codice inline in risorse statiche.

    Se attualmente carichi il tuo CSS completo in modo asincrono con librerie come loadCSS, non è davvero necessario. Con media="print" , puoi indurre il browser a recuperare il CSS in modo asincrono ma applicandolo all'ambiente dello schermo una volta caricato. ( grazie, Scott! )

    <!-- Via Scott Jehl. https://www.filamentgroup.com/lab/load-css-simpler/ --> <!-- Load CSS asynchronously, with low priority --> <link rel="stylesheet" href="full.css" media="print" onload="this.media='all'" />

    Quando si raccolgono tutti i CSS critici per ogni modello, è comune esplorare solo l'area "above-the-fold". Tuttavia, per layout complessi, potrebbe essere una buona idea includere anche le basi del layout per evitare enormi costi di ricalcolo e ridisegno, danneggiando di conseguenza il punteggio di Core Web Vitals.

    Cosa succede se un utente ottiene un URL che si collega direttamente al centro della pagina ma il CSS non è stato ancora scaricato? In tal caso, è diventato comune nascondere i contenuti non critici, ad esempio con l' opacity: 0; in CSS integrato e opacity: 1 nel file CSS completo e visualizzalo quando CSS è disponibile. Tuttavia, ha un grosso svantaggio , poiché gli utenti con connessioni lente potrebbero non essere mai in grado di leggere il contenuto della pagina. Ecco perché è meglio mantenere sempre visibile il contenuto, anche se potrebbe non avere uno stile adeguato.

    L'inserimento di CSS critici (e altre risorse importanti) in un file separato nel dominio principale presenta vantaggi, a volte anche più dell'inlining, a causa della memorizzazione nella cache. Chrome apre in modo speculativo una seconda connessione HTTP al dominio principale quando richiede la pagina, il che elimina la necessità di una connessione TCP per recuperare questo CSS. Ciò significa che puoi creare una serie di file CSS critici (ad esempio critical-homepage.css , critical-product-page.css ecc.) e servirli dalla tua radice, senza doverli incorporare. ( grazie, Filippo! )

    Un avvertimento: con HTTP/2, i CSS critici potrebbero essere archiviati in un file CSS separato e inviati tramite un server push senza gonfiare l'HTML. Il problema è che il push del server è stato problematico con molti trucchi e condizioni di gara su tutti i browser. Non è mai stato supportato in modo coerente e ha avuto alcuni problemi di memorizzazione nella cache (vedi diapositiva 114 in poi della presentazione di Hooman Beheshti).

    L'effetto potrebbe, infatti, essere negativo e gonfiare i buffer di rete, impedendo la consegna di frame autentici nel documento. Quindi non è stato molto sorprendente che per il momento Chrome stia pianificando di rimuovere il supporto per Server Push.

  3. Prova a raggruppare le tue regole CSS.
    Ci siamo abituati ai CSS critici, ma ci sono alcune ottimizzazioni che potrebbero andare oltre. Harry Roberts ha condotto una ricerca notevole con risultati abbastanza sorprendenti. Ad esempio, potrebbe essere una buona idea dividere il file CSS principale nelle sue singole media query. In questo modo, il browser recupererà CSS critici con priorità alta e tutto il resto con priorità bassa, completamente fuori dal percorso critico.

    Inoltre, evita di posizionare <link rel="stylesheet" /> prima degli snippet async . Se gli script non dipendono dai fogli di stile, considera di posizionare gli script di blocco sopra gli stili di blocco. Se lo fanno, dividi quel JavaScript in due e caricalo su entrambi i lati del tuo CSS.

    Scott Jehl ha risolto un altro problema interessante memorizzando nella cache un file CSS integrato con un addetto ai servizi, un problema comune che è familiare se si utilizzano CSS critici. Fondamentalmente, aggiungiamo un attributo ID all'elemento style in modo che sia facile trovarlo usando JavaScript, quindi un piccolo pezzo di JavaScript trova quel CSS e usa l'API Cache per memorizzarlo in una cache del browser locale (con un tipo di contenuto di text/css ) da utilizzare nelle pagine successive. Per evitare l'inlining nelle pagine successive e fare riferimento invece alle risorse memorizzate nella cache esternamente, impostiamo un cookie alla prima visita a un sito. Ecco!

    Vale la pena notare che anche lo stile dinamico può essere costoso, ma di solito solo nei casi in cui ti affidi a centinaia di componenti composti contemporaneamente renderizzati. Quindi, se stai usando CSS-in-JS, assicurati che la tua libreria CSS-in-JS ottimizzi l'esecuzione quando il tuo CSS non ha dipendenze da temi o oggetti di scena e non componi eccessivamente i componenti con stile . Aggelos Arvanitakis condivide ulteriori approfondimenti sui costi delle prestazioni di CSS-in-JS.

  4. Trasmetti in streaming le risposte?
    Spesso dimenticati e trascurati, i flussi forniscono un'interfaccia per leggere o scrivere blocchi di dati asincroni, di cui solo un sottoinsieme potrebbe essere disponibile in memoria in un dato momento. Fondamentalmente, consentono alla pagina che ha effettuato la richiesta originale di iniziare a lavorare con la risposta non appena è disponibile il primo blocco di dati e utilizzano parser ottimizzati per lo streaming per visualizzare progressivamente il contenuto.

    Potremmo creare un flusso da più fonti. Ad esempio, invece di servire una shell dell'interfaccia utente vuota e lasciare che JavaScript la popola, puoi lasciare che l'operatore del servizio costruisca un flusso in cui la shell proviene da una cache, ma il corpo proviene dalla rete. Come ha notato Jeff Posnick, se la tua app Web è alimentata da un CMS che esegue il rendering del server HTML unendo insieme modelli parziali, quel modello si traduce direttamente nell'utilizzo di risposte in streaming, con la logica del modello replicata nel lavoratore del servizio anziché nel tuo server. L'articolo The Year of Web Streams di Jake Archibald evidenzia come esattamente potresti costruirlo. L'aumento delle prestazioni è abbastanza evidente.

    Un importante vantaggio dello streaming dell'intera risposta HTML è che l'HTML visualizzato durante la richiesta di navigazione iniziale può sfruttare appieno il parser HTML in streaming del browser. I blocchi di HTML inseriti in un documento dopo il caricamento della pagina (come è comune con i contenuti popolati tramite JavaScript) non possono trarre vantaggio da questa ottimizzazione.

    Supporto del browser? Ci si arriva ancora con il supporto parziale in Chrome, Firefox, Safari ed Edge che supportano l'API e Service Workers supportati in tutti i browser moderni. E se ti senti di nuovo avventuroso, puoi controllare un'implementazione sperimentale delle richieste di streaming, che ti consente di iniziare a inviare la richiesta mentre continua a generare il corpo. Disponibile in Chrome 85.

Un'immagine che riassume l'utilizzo dei dati di salvataggio su Android Chrome e gli hit o le sessioni img medi scoperti dalla ricerca Cloudinary a novembre 2019 e aprile 2020
Secondo una ricerca Cloudinary, il 18% degli utenti globali di Android Chrome ha la modalità Lite abilitata (aka Save-Data). (Grande anteprima)
  1. Considera di rendere i tuoi componenti sensibili alla connessione.
    I dati possono essere costosi e con il carico utile in aumento, dobbiamo rispettare gli utenti che scelgono di optare per il risparmio dei dati mentre accedono ai nostri siti o app. L'intestazione della richiesta di suggerimento client Save-Data ci consente di personalizzare l'applicazione e il carico utile per utenti con vincoli di costi e prestazioni.

    In effetti, è possibile riscrivere le richieste di immagini con DPI elevati in immagini DPI basse, rimuovere i caratteri Web, effetti di parallasse fantasiosi, visualizzare in anteprima le miniature e lo scorrimento infinito, disattivare la riproduzione automatica dei video, i push del server, ridurre il numero di elementi visualizzati e ridurre la qualità dell'immagine, oppure cambia anche il modo in cui fornisci il markup. Tim Vereecke ha pubblicato un articolo molto dettagliato sulle strategie di data-s(h)aver con molte opzioni per il salvataggio dei dati.

    Chi sta usando save-data , ti starai chiedendo? Il 18% degli utenti globali di Android Chrome ha la modalità Lite abilitata (con il Save-Data attivato) ed è probabile che il numero sia più alto. Secondo la ricerca di Simon Hearne, il tasso di adesione è più alto su dispositivi più economici, ma ci sono molti valori anomali. Ad esempio: gli utenti in Canada hanno un tasso di partecipazione di oltre il 34% (rispetto al 7% circa negli Stati Uniti) e gli utenti dell'ultima ammiraglia Samsung hanno un tasso di partecipazione di quasi il 18% a livello globale.

    Con la modalità Save-Data attiva, Chrome Mobile fornirà un'esperienza ottimizzata, ovvero un'esperienza Web proxy con script differiti , font-display: swap e caricamento lento forzato. È solo più sensato costruire l'esperienza da solo piuttosto che fare affidamento sul browser per effettuare queste ottimizzazioni.

    L'intestazione è attualmente supportata solo in Chromium, sulla versione Android di Chrome o tramite l'estensione Data Saver su un dispositivo desktop. Infine, puoi anche utilizzare l'API Network Information per fornire costosi moduli JavaScript, immagini e video ad alta risoluzione in base al tipo di rete. L'API Network Information e in particolare navigator.connection.effectiveType utilizzano i valori RTT , downlink , effectiveType (e pochi altri) per fornire una rappresentazione della connessione e dei dati che gli utenti possono gestire.

    In questo contesto, Max Bock parla di componenti sensibili alla connessione e Addy Osmani parla di servizio di moduli adattivi. Ad esempio, con React, potremmo scrivere un componente che esegue il rendering in modo diverso per diversi tipi di connessione. Come suggerito da Max, un componente <Media /> in un articolo di notizie potrebbe produrre:

    • Offline : un segnaposto con testo alt ,
    • 2G / modalità save-data : un'immagine a bassa risoluzione,
    • 3G su schermo non retina: un'immagine a media risoluzione,
    • 3G su schermi Retina: immagine Retina ad alta risoluzione,
    • 4G : un video HD.

    Dean Hume fornisce un'implementazione pratica di una logica simile utilizzando un operatore di servizio. Per un video, potremmo visualizzare un poster video per impostazione predefinita, quindi visualizzare l'icona "Riproduci" nonché la shell del lettore video, i metadati del video ecc. su connessioni migliori. Come ripiego per i browser non di supporto, potremmo ascoltare l'evento canplaythrough e utilizzare Promise.race() per interrompere il caricamento del codice sorgente se l'evento canplaythrough non si attiva entro 2 secondi.

    Se vuoi approfondire un po', ecco un paio di risorse per iniziare:

    • Addy Osmani mostra come implementare il servizio adattivo in React.
    • React Adaptive Loading Hooks & Utilities fornisce frammenti di codice per React,
    • Netanel Basel esplora i componenti Connection-Aware in Angular,
    • Theodore Vorilas condivide il funzionamento del servizio di componenti adattivi utilizzando l'API di informazioni di rete in Vue.
    • Umar Hansa mostra come scaricare/eseguire selettivamente JavaScript costoso.
  2. Prendi in considerazione l'idea di rendere i tuoi componenti sensibili alla memoria del dispositivo.
    Tuttavia, la connessione di rete ci offre solo una prospettiva nel contesto dell'utente. Andando oltre, puoi anche regolare dinamicamente le risorse in base alla memoria del dispositivo disponibile, con l'API Device Memory. navigator.deviceMemory restituisce quanta RAM ha il dispositivo in gigabyte, arrotondata per difetto alla potenza di due più vicina. L'API dispone anche di un'intestazione di suggerimenti client, Device-Memory , che riporta lo stesso valore.

    Bonus : Umar Hansa mostra come rinviare script costosi con importazioni dinamiche per modificare l'esperienza in base alla memoria del dispositivo, alla connettività di rete e alla concorrenza hardware.

Un'analisi che mostra come vengono assegnate priorità alle diverse risorse in Blink a partire da Chrome 46 e versioni successive
Un'analisi che mostra come vengono assegnate priorità alle diverse risorse in Blink a partire da Chrome 46 e versioni successive. (Credito immagine: Addy Osmani) (Anteprima grande)
  1. Riscalda la connessione per accelerare la consegna.
    Usa i suggerimenti per le risorse per risparmiare tempo su dns-prefetch (che esegue una ricerca DNS in background), preconnect (che chiede al browser di avviare l'handshake della connessione (DNS, TCP, TLS) in background), prefetch (che chiede al browser per richiedere una risorsa) e preload (che precarica le risorse senza eseguirle, tra le altre cose). Ben supportato nei browser moderni, con il supporto in arrivo a breve su Firefox.

    Ricordi prerender ? Il suggerimento sulla risorsa utilizzato per richiedere al browser di creare l'intera pagina in background per la navigazione successiva. I problemi di implementazione erano piuttosto problematici, da un'enorme impronta di memoria e utilizzo della larghezza di banda a più hit di analisi registrati e impressioni degli annunci.

    Non sorprende che sia stato deprecato, ma il team di Chrome lo ha riportato come meccanismo NoState Prefetch. In effetti, Chrome tratta invece il suggerimento di prerender come un NoState Prefetch, quindi possiamo usarlo ancora oggi. Come spiega Katie Hempenius in quell'articolo, "come il prerendering, NoState Prefetch recupera le risorse in anticipo ; ma a differenza del prerendering, non esegue JavaScript né esegue il rendering di alcuna parte della pagina in anticipo".

    NoState Prefetch utilizza solo ~45MiB di memoria e le sottorisorse recuperate verranno recuperate con una priorità di rete IDLE . Da Chrome 69, NoState Prefetch aggiunge l'intestazione Scopo: Prefetch a tutte le richieste per renderle distinguibili dalla normale navigazione.

    Inoltre, fai attenzione alle alternative e ai portali di prerendering, un nuovo sforzo verso il prerendering attento alla privacy, che fornirà l' preview del contenuto per una navigazione senza interruzioni.

    L'utilizzo dei suggerimenti per le risorse è probabilmente il modo più semplice per aumentare le prestazioni e funziona davvero bene. Quando usare cosa? Come ha spiegato Addy Osmani, è ragionevole precaricare risorse che sappiamo molto probabilmente saranno utilizzate nella pagina corrente e per navigazioni future attraverso più confini di navigazione, ad esempio i pacchetti Webpack necessari per le pagine che l'utente non ha ancora visitato.

    L'articolo di Addy su "Caricamento delle priorità in Chrome" mostra in che modo Chrome interpreta esattamente i suggerimenti sulle risorse, quindi una volta che hai deciso quali risorse sono fondamentali per il rendering, puoi assegnare loro una priorità elevata. Per vedere la priorità delle tue richieste, puoi abilitare una colonna "priorità" nella tabella delle richieste di rete di Chrome DevTools (oltre a Safari).

    La maggior parte delle volte in questi giorni, utilizzeremo almeno preconnect e dns-prefetch e saremo cauti nell'usare prefetch , preload e prerender . Nota che anche con preconnect e dns-prefetch , il browser ha un limite al numero di host a cui cercherà/si connetterà in parallelo, quindi è una scommessa sicura ordinarli in base alla priorità ( grazie Philip Tellis! ).

    Poiché i caratteri di solito sono risorse importanti in una pagina, a volte è una buona idea richiedere al browser di scaricare i caratteri critici con il preload . Tuttavia, ricontrolla se aiuta effettivamente le prestazioni poiché esiste un puzzle di priorità durante il precaricamento dei caratteri: poiché il preload è considerato di grande importanza, può scavalcare risorse ancora più critiche come CSS critici. ( grazie, Barry! )

    <!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
    <!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />

    Poiché <link rel="preload"> accetta un attributo media , puoi scegliere di scaricare selettivamente le risorse in base alle regole di query @media , come mostrato sopra.

    Inoltre, possiamo utilizzare gli attributi imagesrcset e imagesizes per precaricare più rapidamente le immagini degli eroi scoperte in ritardo o qualsiasi immagine caricata tramite JavaScript, ad esempio le locandine dei film:

    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>

    Possiamo anche precaricare il JSON come fetch , quindi viene scoperto prima che JavaScript lo richieda:

    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="fetch" href="foo.com/api/movies.json" crossorigin>

    Potremmo anche caricare JavaScript in modo dinamico, in modo efficace per l'esecuzione pigra dello script.

    /* Adding a preload hint to the head */ var preload = document.createElement("link"); link.href = "myscript.js"; link.rel = "preload"; link.as = "script"; document.head.appendChild(link); /* Injecting a script when we want it to execute */ var script = document.createElement("script"); script.src = "myscript.js"; document.body.appendChild(script);

    Alcuni accorgimenti da tenere a mente: il preload è utile per spostare il tempo di inizio download di una risorsa più vicino alla richiesta iniziale, ma le risorse precaricate finiscono nella cache di memoria che è legata alla pagina che effettua la richiesta. il preload funziona bene con la cache HTTP: una richiesta di rete non viene mai inviata se l'elemento è già presente nella cache HTTP.

    Pertanto, è utile per le risorse scoperte in ritardo, le immagini hero caricate tramite background-image , l'integrazione di CSS (o JavaScript) critici e il precaricamento del resto del CSS (o JavaScript).

    Un esempio che utilizza la copertina del film Greyhound con Tom Hanks per mostrare che le immagini precaricate vengono caricate prima poiché non è necessario attendere JavaScript per scoprirle
    Precarica le immagini importanti in anticipo; non c'è bisogno di aspettare JavaScript per scoprirli. (Credito immagine: "Precarica più velocemente le immagini dell'eroe scoperte in ritardo" di Addy Osmani) (Anteprima grande)

    Un tag di preload può avviare un precaricamento solo dopo che il browser ha ricevuto l'HTML dal server e il parser lookahead ha trovato il tag di preload . Il precaricamento tramite l'intestazione HTTP potrebbe essere un po' più veloce poiché non dobbiamo aspettare che il browser analizzi l'HTML per avviare la richiesta (è dibattuto però).

    I primi suggerimenti aiuteranno ulteriormente, consentendo al precaricamento di attivarsi anche prima dell'invio delle intestazioni di risposta per l'HTML (sulla roadmap in Chromium, Firefox). Inoltre, i suggerimenti per le priorità ci aiuteranno a indicare le priorità di caricamento degli script.

    Attenzione : se stai usando preload , as deve essere definito o non viene caricato nulla, inoltre i caratteri precaricati senza l'attributo crossorigin verranno recuperati due volte. Se stai utilizzando prefetch , fai attenzione ai problemi di intestazione Age in Firefox.

Un grafico che mostra il primo disegno di contenuto (in base allo stato di lavoro del server) con un conteggio da 0 a 150 in un determinato periodo di tempo (in ms)
Con un addetto ai servizi, possiamo richiedere solo il minimo indispensabile di dati e quindi trasformare tali dati in un documento HTML completo per migliorare FCP. (via Phil Walton) (Grande anteprima)
  1. Usa i service worker per la memorizzazione nella cache e i fallback di rete.
    Nessuna ottimizzazione delle prestazioni su una rete può essere più veloce di una cache archiviata localmente sulla macchina di un utente (ci sono però delle eccezioni). Se il tuo sito Web è in esecuzione su HTTPS, possiamo memorizzare nella cache le risorse statiche in una cache di servizio e archiviare i fallback offline (o anche le pagine offline) e recuperarli dalla macchina dell'utente, invece di andare in rete.

    Come suggerito da Phil Walton, con gli addetti ai servizi possiamo inviare payload HTML più piccoli generando in modo programmatico le nostre risposte. Un addetto al servizio può richiedere solo il minimo indispensabile di dati di cui ha bisogno dal server (ad esempio un contenuto HTML parziale, un file Markdown, dati JSON, ecc.), e quindi può trasformare a livello di codice quei dati in un documento HTML completo. Pertanto, una volta che un utente visita un sito e l'operatore del servizio è installato, l'utente non richiederà mai più una pagina HTML completa. L'impatto sulle prestazioni può essere piuttosto impressionante.

    Supporto del browser? Gli operatori dei servizi sono ampiamente supportati e il fallback è comunque la rete. Aiuta a migliorare le prestazioni ? Eh si, lo fa. E sta migliorando, ad esempio con il recupero in background che consente caricamenti/download in background anche tramite un addetto al servizio.

    Esistono diversi casi d'uso per un lavoratore del servizio. Ad esempio, potresti implementare la funzione "Salva per offline", gestire immagini interrotte, introdurre messaggi tra le schede o fornire strategie di memorizzazione nella cache diverse in base ai tipi di richiesta. In generale, una strategia comune affidabile consiste nell'archiviazione della shell dell'app nella cache del lavoratore del servizio insieme ad alcune pagine critiche, come la pagina offline, la prima pagina e qualsiasi altra cosa che potrebbe essere importante nel tuo caso.

    Ci sono però alcuni aspetti da tenere a mente. Con un operatore di servizio in atto, dobbiamo fare attenzione alle richieste di intervallo in Safari (se stai utilizzando Workbox per un operatore di servizio, ha un modulo di richiesta di intervallo). Se ti sei mai imbattuto in DOMException: Quota exceeded. errore nella console del browser, quindi esamina l'articolo di Gerardo Quando 7 KB equivalgono a 7 MB.

    Come scrive Gerardo, "Se stai creando un'app Web progressiva e stai riscontrando un'archiviazione della cache gonfia quando il tuo addetto ai servizi memorizza nella cache le risorse statiche servite dalle CDN, assicurati che esista l'intestazione di risposta CORS corretta per le risorse multiorigine, non memorizzi nella cache le risposte opache con il tuo addetto ai servizi involontariamente, attivi le risorse immagine cross-origin in modalità CORS aggiungendo l'attributo crossorigin al tag <img> ".

    Ci sono molte ottime risorse per iniziare con gli addetti ai servizi:

    • Service Worker Mindset, che ti aiuta a capire come lavorano gli operatori di servizio dietro le quinte e le cose da capire quando ne costruiscono uno.
    • Chris Ferdinandi fornisce un'ampia serie di articoli sugli operatori del servizio, spiegando come creare applicazioni offline e coprendo una varietà di scenari, dal salvataggio offline delle pagine visualizzate di recente all'impostazione di una data di scadenza per gli elementi in una cache di un lavoratore del servizio.

    • Insidie ​​e best practice per gli operatori del servizio, con alcuni suggerimenti sull'ambito, il ritardo della registrazione di un lavoratore del servizio e la memorizzazione nella cache del lavoratore del servizio.
    • Grande serie di Ire Aderinokun su "Offline First" con Service Worker, con una strategia sul precaching della shell dell'app.
    • Service Worker: un'introduzione con suggerimenti pratici su come utilizzare Service Worker per esperienze offline avanzate, sincronizzazioni periodiche in background e notifiche push.
    • Vale sempre la pena fare riferimento al buon vecchio libro di cucina offline di Jake Archibald con una serie di ricette su come cuocere il tuo personale di servizio.
    • Workbox è un set di librerie di operatori di servizio create appositamente per la creazione di app Web progressive.
  2. Stai eseguendo server worker sulla CDN/Edge, ad esempio per test A/B?
    A questo punto, siamo abbastanza abituati a eseguire i service worker sul client, ma con le CDN che li implementano sul server, potremmo usarli per modificare le prestazioni anche al limite.

    Ad esempio, nei test A/B, quando HTML deve variare il suo contenuto per utenti diversi, potremmo utilizzare Service Workers sui server CDN per gestire la logica. Potremmo anche eseguire lo streaming della riscrittura HTML per velocizzare i siti che utilizzano Google Fonts.

Un grafico che mostra le serie temporali delle installazioni degli addetti ai servizi su desktop e dispositivi mobili con la percentuale di pagine nel tempo tra gennaio 2016 e luglio 2020
Serie temporali dell'installazione dell'operatore di servizio. Solo lo 0,87% di tutte le pagine desktop registra un addetto ai servizi, secondo Web Almanac. (Grande anteprima)
  1. Ottimizza le prestazioni di rendering.
    Ogni volta che l'applicazione è lenta, si nota subito. Quindi dobbiamo assicurarci che non ci siano ritardi durante lo scorrimento della pagina o quando un elemento è animato e che stai raggiungendo costantemente 60 fotogrammi al secondo. Se ciò non è possibile, è preferibile almeno rendere coerenti i fotogrammi al secondo rispetto a un intervallo misto compreso tra 60 e 15. Usa will-change dei CSS per informare il browser di quali elementi e proprietà cambieranno.

    Ogni volta che si verificano, eseguire il debug di ridisegni non necessari in DevTools:

    • Misura le prestazioni di rendering in runtime. Dai un'occhiata ad alcuni suggerimenti utili su come dargli un senso.
    • Per iniziare, dai un'occhiata al corso gratuito Udacity di Paul Lewis sull'ottimizzazione del rendering del browser e all'articolo di Georgy Marchuk sulla pittura del browser e considerazioni per le prestazioni web.
    • Abilita Paint Flashing in "Altri strumenti → Rendering → Paint Flashing" in Firefox DevTools.
    • In React DevTools, seleziona "Evidenzia aggiornamenti" e abilita "Registra perché ogni componente è stato renderizzato",
    • Puoi anche utilizzare Why Did You Render, quindi quando un componente viene rieseguito, un flash ti avviserà della modifica.

    Stai usando un layout in muratura? Tieni presente che potrebbe essere in grado di costruire un layout in muratura con la sola griglia CSS, molto presto.

    Se vuoi approfondire l'argomento, Nolan Lawson ha condiviso trucchi per misurare accuratamente le prestazioni del layout nel suo articolo e anche Jason Miller ha suggerito tecniche alternative. Abbiamo anche un piccolo articolo di Sergey Chikuyonok su come ottenere correttamente l'animazione della GPU.

    Animazioni ad alte prestazioni tra cui Posizione, Scala, Rotazione e Opacità
    I browser possono animare la trasformazione e l'opacità a buon mercato. Trigger CSS è utile per verificare se CSS attiva re-layout o reflow. (Credito immagine: Addy Osmani) (Anteprima grande)

    Nota : le modifiche ai livelli composti da GPU sono le meno costose, quindi se riesci a farla franca attivando solo la composizione tramite opacity e transform , sarai sulla strada giusta. Anche Anna Migas ha fornito molti consigli pratici nel suo intervento sul debug delle prestazioni di rendering dell'interfaccia utente. E per capire come eseguire il debug delle prestazioni della vernice in DevTools, controlla il video di controllo delle prestazioni della vernice di Umar.

  2. Hai ottimizzato per le prestazioni percepite?
    Sebbene la sequenza di come i componenti appaiono sulla pagina e la strategia di come serviamo le risorse al browser sono importanti, non dovremmo sottovalutare anche il ruolo delle prestazioni percepite. Il concetto si occupa degli aspetti psicologici dell'attesa, fondamentalmente mantenendo i clienti occupati o coinvolti mentre sta accadendo qualcos'altro. È qui che entrano in gioco la gestione della percezione, l'avvio preventivo, il completamento anticipato e la gestione della tolleranza.

    Che cosa significa tutto questo? Durante il caricamento delle risorse, possiamo cercare di essere sempre un passo avanti rispetto al cliente, in modo che l'esperienza sia rapida mentre succedono molte cose in background. Per mantenere il cliente impegnato, possiamo testare schermate dello scheletro (demo di implementazione) invece di caricare indicatori, aggiungere transizioni/animazioni e sostanzialmente imbrogliare l'UX quando non c'è più niente da ottimizzare.

    Nel loro caso di studio su The Art of UI Skeletons, Kumar McMillan condivide alcune idee e tecniche su come simulare elenchi dinamici, testo e schermo finale, oltre a come considerare il pensiero scheletrico con React.

    Attenzione però: gli schermi dello scheletro devono essere testati prima dell'implementazione poiché alcuni test hanno dimostrato che gli schermi dello scheletro possono avere le prestazioni peggiori in base a tutte le metriche.

  3. Impedite spostamenti di layout e ridipinture?
    Nel regno delle prestazioni percepite, probabilmente una delle esperienze più dirompenti è lo spostamento del layout , o reflow , causato da immagini e video ridimensionati, font Web, pubblicità iniettata o script scoperti in ritardo che popolano i componenti con contenuto reale. Di conseguenza, un cliente potrebbe iniziare a leggere un articolo solo per essere interrotto da un salto di layout sopra l'area di lettura. L'esperienza è spesso brusca e abbastanza disorientante: e questo è probabilmente un caso di priorità di caricamento che devono essere riconsiderate.

    La community ha sviluppato un paio di tecniche e soluzioni alternative per evitare reflow. In generale, è una buona idea evitare di inserire nuovi contenuti al di sopra dei contenuti esistenti , a meno che ciò non avvenga in risposta a un'interazione dell'utente. Imposta sempre gli attributi di larghezza e altezza sulle immagini, quindi i browser moderni assegnano la casella e riservano lo spazio per impostazione predefinita (Firefox, Chrome).

    Sia per le immagini che per i video, possiamo utilizzare un segnaposto SVG per riservare la casella di visualizzazione in cui apparirà il file multimediale. Ciò significa che l'area verrà riservata correttamente quando è necessario mantenere anche le sue proporzioni. Possiamo anche utilizzare segnaposto o immagini di riserva per annunci e contenuti dinamici, nonché slot di layout preassegnati.

    Invece di caricare lazy le immagini con script esterni, prendere in considerazione l'utilizzo del lazy-loading nativo o del lazy-loading ibrido quando carichiamo uno script esterno solo se il lazy-loading nativo non è supportato.

    Come accennato in precedenza, raggruppa sempre i ridisegni dei caratteri Web e passa da tutti i caratteri di fallback a tutti i caratteri Web contemporaneamente: assicurati solo che il passaggio non sia troppo brusco, regolando l'altezza della linea e la spaziatura tra i caratteri con il font-style-matcher .

    Per sovrascrivere le metriche dei caratteri per un carattere di riserva per emulare un carattere Web, possiamo utilizzare i descrittori @font-face per sovrascrivere le metriche dei caratteri (demo, abilitato in Chrome 87). (Si noti che le regolazioni sono complicate con pile di caratteri complicate.)

    Per i CSS tardivi, possiamo garantire che i CSS critici per il layout siano integrati nell'intestazione di ciascun modello. Anche oltre: per le pagine lunghe, quando viene aggiunta la barra di scorrimento verticale, sposta il contenuto principale di 16px a sinistra. Per visualizzare una barra di scorrimento in anticipo, possiamo aggiungere overflow-y: scroll su html per applicare una barra di scorrimento al primo disegno. Quest'ultimo aiuta perché le barre di scorrimento possono causare spostamenti di layout non banali a causa del riflusso del contenuto above the fold quando la larghezza cambia. Dovrebbe accadere principalmente su piattaforme con barre di scorrimento non sovrapposte come Windows. Ma: interrompe la position: sticky perché quegli elementi non scorreranno mai fuori dal contenitore.

    Se hai a che fare con intestazioni che diventano fisse o posizionate appiccicose nella parte superiore della pagina durante lo scorrimento, riserva spazio per l'intestazione quando diventa sbiadita, ad esempio con un elemento segnaposto o un margin-top sul contenuto. Un'eccezione dovrebbero essere i banner di consenso ai cookie che non dovrebbero avere impatto su CLS, ma a volte lo fanno: dipende dall'implementazione. Ci sono alcune strategie interessanti e takeaway in questo thread di Twitter.

    Per un componente di schede che potrebbe includere varie quantità di testo, puoi impedire i cambiamenti di layout con le pile di griglie CSS. Posizionando il contenuto di ciascuna scheda nella stessa area della griglia e nascondendone una alla volta, possiamo garantire che il contenitore occupi sempre l'altezza dell'elemento più grande, quindi non si verificheranno spostamenti di layout.

    Ah, e, naturalmente, lo scorrimento infinito e "Carica altro" possono causare anche spostamenti di layout se c'è contenuto sotto l'elenco (es. piè di pagina). Per migliorare CLS, riserva spazio sufficiente per il contenuto che verrebbe caricato prima che l'utente scorri fino a quella parte della pagina, rimuovi il piè di pagina o qualsiasi elemento DOM nella parte inferiore della pagina che potrebbe essere spinto verso il basso dal caricamento del contenuto. Inoltre, precarica i dati e le immagini per i contenuti below-the-fold in modo che quando un utente scorre così lontano, è già lì. Puoi utilizzare le librerie di virtualizzazione degli elenchi come react-window per ottimizzare anche elenchi lunghi ( grazie, Addy Osmani! ).

    Per garantire che l'impatto dei reflow sia contenuto, misurare la stabilità del layout con l'API Layout Instability. Con esso, puoi calcolare il punteggio Cumulative Layout Shift ( CLS ) e includerlo come requisito nei tuoi test, quindi ogni volta che viene visualizzata una regressione, puoi tracciarla e risolverla.

    Per calcolare il punteggio di spostamento del layout, il browser esamina le dimensioni della finestra e il movimento degli elementi instabili nella finestra tra due fotogrammi renderizzati. Idealmente, il punteggio sarebbe vicino a 0 . C'è un'ottima guida di Milica Mihajlija e Philip Walton su cos'è il CLS e come misurarlo. È un buon punto di partenza per misurare e mantenere le prestazioni percepite ed evitare interruzioni, soprattutto per le attività business-critical.

    Suggerimento rapido : per scoprire cosa ha causato un cambiamento di layout in DevTools, puoi esplorare i cambiamenti di layout in "Esperienza" nel pannello delle prestazioni.

    Bonus : se vuoi ridurre i reflow e le ridipinture, controlla la guida di Charis Theodoulou per ridurre al minimo il DOM Reflow/Layout Thrashing e l'elenco di Paul Irish di What force layout / reflow così come CSSTriggers.com, una tabella di riferimento sulle proprietà CSS che attivano il layout, paint e compositing.

Sommario

  1. Prepararsi: pianificazione e metriche
  2. Stabilire obiettivi realistici
  3. Definire l'ambiente
  4. Ottimizzazioni degli asset
  5. Costruisci ottimizzazioni
  6. Ottimizzazioni di consegna
  7. Rete, HTTP/2, HTTP/3
  8. Test e monitoraggio
  9. Vittorie veloci
  10. Tutto in una pagina
  11. Scarica la lista di controllo (PDF, Apple Pages, MS Word)
  12. Iscriviti alla nostra newsletter via email per non perdere le prossime guide.