Man mano che creiamo siti che si affidano maggiormente a JavaScript, a volte paghiamo per ciò che inviamo in modi che non sempre possiamo vedere facilmente. In questo articolo, spiegheremo perché un po' di disciplina può aiutarti se vuoi che il tuo sito si carichi e sia interattivo rapidamente sui dispositivi mobili. La distribuzione di meno JavaScript può significare meno tempo per la trasmissione di rete, meno tempo per la decompressione del codice e meno tempo per l'analisi e la compilazione di questo JavaScript.
Rete
Quando la maggior parte degli sviluppatori pensa al costo di JavaScript, lo fa in termini di costo di download ed esecuzione. L'invio di più byte di JavaScript tramite la rete richiede più tempo, più lenta è la connessione di un utente.

Questo può essere un problema, in quanto il tipo di connessione di rete effettivo di un utente potrebbe non essere effettivamente 3G, 4G o Wi-Fi. Puoi essere connesso al Wi-Fi di un bar, ma anche a un hotspot di rete dati con velocità 2G.
Puoi ridurre il costo di trasferimento di rete di JavaScript tramite:
- Inviare solo il codice necessario all'utente.
- Utilizza la suddivisione del codice per suddividere il codice JavaScript in ciò che è fondamentale e ciò che non lo è. I bundle di moduli come webpack supportano la suddivisione del codice.
- Caricamento lento del codice non critico.
- Minimizzazione
- Utilizza UglifyJS per minimizzare il codice ES5.
- Utilizza babel-minify o uglify-es per minimizzare ES2015+.
- Compressione
- Rimuovi il codice inutilizzato.
- Identifica le opportunità per il codice che può essere rimosso o caricato in modo lento con la copertura del codice di DevTools.
- Utilizza babel-preset-env e browserlist per evitare di eseguire il transpiling di funzionalità già presenti nei browser moderni. Gli sviluppatori esperti potrebbero trovare utile un'attenta analisi dei loro bundle webpack per identificare le opportunità di eliminare le dipendenze non necessarie.
- Per lo stripping del codice, consulta tree-shaking, le ottimizzazioni avanzate di Closure Compiler e i plug-in di riduzione delle librerie come lodash-babel-plugin o ContextReplacementPlugin di webpack per librerie come Moment.js.
- Memorizzazione del codice nella cache per ridurre al minimo i passaggi di rete.
- Utilizza la memorizzazione nella cache HTTP per assicurarti che i browser memorizzino nella cache le risposte in modo efficace. Determina la durata ottimale degli script (max-age) e fornisci token di convalida (ETag) per evitare di trasferire byte invariati.
- La memorizzazione nella cache dei service worker può rendere la tua app resiliente alla rete e darti accesso immediato a funzionalità come la cache del codice di V8.
- Utilizza la memorizzazione nella cache a lungo termine per evitare di dover recuperare nuovamente le risorse che non sono cambiate. Se utilizzi Webpack, consulta l'articolo sull'hashing dei nomi file.
Analizza/compila
Una volta scaricato, uno dei costi più elevati di JavaScript è il tempo necessario a un motore JS per analizzare/compilare questo codice. In Chrome DevTools, l'analisi e la compilazione fanno parte del tempo di "Scripting" giallo nel riquadro Prestazioni.

Le schede Bottom-Up e Albero delle chiamate mostrano i tempi esatti di analisi/compilazione:
Ma perché è importante?

Un tempo di analisi/compilazione del codice lungo può ritardare notevolmente il momento in cui un utente può interagire con il tuo sito. Più JavaScript invii, più tempo sarà necessario per analizzarlo e compilarlo prima che il tuo sito diventi interattivo.
Byte per byte, JavaScript è più costoso da elaborare per il browser rispetto a un'immagine o un web font di dimensioni equivalenti - Tom Dale
Rispetto a JavaScript, l'elaborazione di immagini di dimensioni equivalenti comporta numerosi costi (devono comunque essere decodificate), ma sull'hardware mobile medio, JS ha maggiori probabilità di influire negativamente sull'interattività di una pagina.
Quando diciamo che l'analisi e la compilazione sono lente, il contesto è importante: stiamo parlando di smartphone medi. Gli utenti medi possono avere smartphone con CPU e GPU lente, senza cache L2/L3 e che potrebbero persino avere vincoli di memoria.
Le funzionalità di rete e quelle del dispositivo non sempre corrispondono. Un utente con una connessione Fiber straordinaria non ha necessariamente la CPU migliore per analizzare e valutare il codice JavaScript inviato al suo dispositivo. Questo vale anche al contrario: una connessione di rete pessima, ma una CPU velocissima. — Kristofer Baxter, LinkedIn
Di seguito possiamo vedere il costo dell'analisi di circa 1 MB di JavaScript decompresso (semplice) su hardware di fascia bassa e alta. Esiste una differenza di 2-5 volte nel tempo di analisi/compilazione del codice tra gli smartphone più veloci sul mercato e quelli medi.
E un sito reale, come CNN.com?
Sull'iPhone 8 di fascia alta, l'analisi/compilazione del codice JS di CNN richiede solo circa 4 secondi, rispetto ai circa 13 secondi di uno smartphone medio (Moto G4). Questo può influire notevolmente sulla rapidità con cui un utente può interagire completamente con il sito.
Questo evidenzia l'importanza di eseguire test su hardware di livello medio (come Moto G4) anziché solo sullo smartphone che potresti avere in tasca. Tuttavia, il contesto è importante: ottimizza per le condizioni del dispositivo e della rete dei tuoi utenti.
Stiamo davvero inviando troppo codice JavaScript? Ehm, forse :)
Utilizzando HTTP Archive (circa 500.000 siti principali) per analizzare lo stato di JavaScript sui dispositivi mobili, possiamo vedere che il 50% dei siti impiega più di 14 secondi per diventare interattivo. Questi siti impiegano fino a 4 secondi solo per analizzare e compilare JS.

Se si tiene conto del tempo necessario per recuperare ed elaborare JS e altre risorse, non sorprende che gli utenti possano dover attendere un po' prima di avere la sensazione che le pagine siano pronte per l'uso. Possiamo sicuramente fare di meglio.
La rimozione di JavaScript non critico dalle pagine può ridurre i tempi di trasmissione, l'analisi e la compilazione che richiedono un uso intensivo della CPU e il potenziale overhead della memoria. In questo modo, le tue pagine diventano interattive più rapidamente.
Tempo di esecuzione
Non sono solo l'analisi e la compilazione che possono avere un costo. L'esecuzione di JavaScript (l'esecuzione del codice una volta analizzato/compilato) è una delle operazioni che devono essere eseguite sul thread principale. Tempi di esecuzione lunghi possono anche ritardare il momento in cui un utente può interagire con il tuo sito.

Se lo script viene eseguito per più di 50 ms, il tempo di interattività viene ritardato dell'intero tempo necessario per scaricare, compilare ed eseguire il JS. Alex Russell
Per risolvere questo problema, è utile suddividere JavaScript in piccoli blocchi per evitare di bloccare il thread principale. Verifica se puoi ridurre la quantità di lavoro svolto durante l'esecuzione.
Altri costi
JavaScript può influire sulle prestazioni della pagina in altri modi:
- Memoria. Le pagine possono sembrare instabili o interrompersi di frequente a causa della garbage collection (GC). Quando un browser recupera memoria, l'esecuzione di JS viene messa in pausa, quindi un browser che esegue spesso la garbage collection può mettere in pausa l'esecuzione più spesso di quanto vorremmo. Evita le perdite di memoria e le frequenti pause di garbage collection per evitare i jank nelle pagine.
- Durante il runtime, il codice JavaScript con tempi di esecuzione lunghi può bloccare il thread principale, causando
pagine che non rispondono. La suddivisione del lavoro in parti più piccole (utilizzando
requestAnimationFrame()orequestIdleCallback()per la pianificazione) può ridurre al minimo i problemi di reattività, il che può contribuire a migliorare l'Interaction to Next Paint (INP).
Pattern per ridurre i costi di pubblicazione di JavaScript
Quando cerchi di mantenere bassi i tempi di analisi/compilazione e di trasmissione di rete per JavaScript, esistono pattern che possono aiutarti, come il chunking basato su route o PRPL.
PRPL
PRPL (Push, Render, Pre-cache, Lazy-load) è un pattern che ottimizza l'interattività tramite la suddivisione del codice e la pre-memorizzazione nella cache aggressive:

Visualizziamo l'impatto che può avere.
Analizziamo il tempo di caricamento dei siti mobile e delle app web progressive più popolari utilizzando le statistiche sulle chiamate di runtime di V8. Come possiamo vedere, il tempo di analisi (mostrato in arancione) rappresenta una parte significativa del tempo impiegato da molti di questi siti:

Wego, un sito che utilizza PRPL, riesce a mantenere un tempo di analisi basso per i suoi percorsi, diventando interattivo molto rapidamente. Molti degli altri siti sopra citati hanno adottato la suddivisione del codice e i budget delle prestazioni per cercare di ridurre i costi di JS.
Bootstrapping progressivo
Molti siti ottimizzano la visibilità dei contenuti a scapito dell'interattività. Per ottenere un first paint rapido quando hai bundle JavaScript di grandi dimensioni, a volte gli sviluppatori utilizzano il rendering lato server, quindi eseguono l'upgrade per collegare i gestori di eventi quando il codice JavaScript viene finalmente recuperato.
Fai attenzione: questa operazione comporta dei costi. 1) In genere invii una risposta HTML più grande che può influire sull'interattività, 2) puoi lasciare l'utente in una situazione di incertezza in cui metà dell'esperienza non può essere effettivamente interattiva finché JavaScript non termina l'elaborazione.
Il bootstrap progressivo potrebbe essere un approccio migliore. Invia una pagina con funzionalità minime (composta solo dal codice HTML/JS/CSS necessario per la route corrente). Man mano che arrivano altre risorse, l'app può eseguire il caricamento lento e sbloccare altre funzionalità.
Il caricamento del codice proporzionale a ciò che è visibile è il Santo Graal. PRPL e il bootstrapping progressivo sono pattern che possono aiutarti a raggiungere questo obiettivo.
Conclusioni
Le dimensioni di trasmissione sono fondamentali per le reti di fascia bassa. Il tempo di analisi è importante per i dispositivi con limitazioni della CPU. Mantenere bassi questi valori è importante.
I team hanno ottenuto risultati positivi adottando budget delle prestazioni rigorosi per mantenere bassi i tempi di trasmissione e analisi/compilazione di JavaScript. Consulta l'articolo "Can You Afford It?: Real-world Web Performance Budgets" per indicazioni sui budget per i dispositivi mobili.
Se stai creando un sito destinato ai dispositivi mobili, fai del tuo meglio per svilupparlo su un hardware rappresentativo, mantieni bassi i tempi di analisi/compilazione di JavaScript e adotta un budget delle prestazioni per assicurarti che il tuo team sia in grado di tenere sotto controllo i costi di JavaScript.
Scopri di più
- Chrome Dev Summit 2017 - Modern Loading Best Practices
- Prestazioni di avvio di JavaScript
- Solving the web performance crisis — Nolan Lawson
- Puoi permettertelo? Real-world performance budgets — Alex Russell
- Evaluating web frameworks and libraries — Kristofer Baxter
- Risultati della sperimentazione di Cloudflare con Brotli per la compressione (tieni presente che Brotli dinamico con una qualità superiore può ritardare il rendering iniziale della pagina, quindi valuta attentamente). In questo caso, è preferibile utilizzare la compressione statica.
- Performance Futures — Sam Saccone