Il caricamento di risorse JavaScript di grandi dimensioni influisce notevolmente sulla velocità della pagina. Divisione il codice JavaScript in blocchi più piccoli e scaricando solo ciò che è necessario che una pagina funzioni durante l'avvio può migliorare notevolmente il carico della pagina reattività, che a sua volta può migliorare l'interazione con la pagina Successivo Verniciatura (INP).
Quando una pagina scarica, analizza e compila file JavaScript di grandi dimensioni, può diventare non risponde per periodi di tempo. Gli elementi della pagina sono visibili, poiché rappresentano parte del codice HTML iniziale di una pagina e definita da CSS. Tuttavia, poiché il codice JavaScript necessari per alimentare questi elementi interattivi, oltre ad altri script caricati della pagina—potrebbero analizzare ed eseguire JavaScript per poter funzionare. La il risultato è che l'utente potrebbe avere la sensazione che l'interazione sia stata significativamente in ritardo o addirittura guastarsi.
Questo accade spesso perché il thread principale è bloccato, in quanto JavaScript viene analizzato e compilate sul thread principale. Se il processo richiede troppo tempo, gli elementi della pagina potrebbero non rispondere abbastanza rapidamente all'input dell'utente. Un rimedio a questo problema è caricare solo il codice JavaScript necessario affinché la pagina funzioni, mentre rimandare altro codice JavaScript per caricarsi in un secondo momento attraverso una tecnica nota come codice la suddivisione. Questo modulo si concentra sull'ultima di queste due tecniche.
Riduci l'analisi e l'esecuzione di JavaScript durante l'avvio mediante la suddivisione del codice
Lighthouse genera un avviso quando l'esecuzione di JavaScript richiede più di 2 secondi e non riesce quando ci vogliono più di 3, 5 secondi. JavaScript eccessivo l'analisi e l'esecuzione sono potenziali problemi in qualsiasi punto della pagina del ciclo di vita, poiché può aumentare il ritardo dell'input di un'interazione. se il momento in cui l'utente interagisce con la pagina coincide con il momento le attività dei thread principali responsabili dell'elaborazione e dell'esecuzione di JavaScript sono in esecuzione.
Inoltre, l'esecuzione e l'analisi eccessive di JavaScript sono particolarmente problematico durante il caricamento iniziale della pagina, in quanto questo è il punto della pagina un ciclo di vita che è probabile che gli utenti interagiscano con la pagina. Infatti, Il tempo di blocco totale (TBT), una metrica della reattività al caricamento, è molto correlato. con INP, il che indica che gli utenti hanno una forte tendenza a tentare interazioni durante il caricamento iniziale della pagina.
Il controllo di Lighthouse che segnala il tempo speso per l'esecuzione di ciascun file JavaScript le richieste di pagina sono utili perché possono aiutarti a identificare esattamente quali possono essere candidati per la suddivisione del codice. Puoi quindi procedere oltre usando lo strumento di copertura in Chrome DevTools per identificare esattamente quali parti di il codice JavaScript di una pagina rimane inutilizzato durante il caricamento della pagina.
La suddivisione del codice è una tecnica utile che può ridurre il codice JavaScript iniziale di una pagina e carichi di lavoro superflui. Consente di suddividere un bundle JavaScript in due parti:
- Il codice JavaScript necessario durante il caricamento della pagina, pertanto non può essere caricato in nessun altro nel tempo.
- JavaScript rimanente che può essere caricato in un secondo momento, molto spesso nel punto in cui l'utente interagisce con un determinato elemento interattivo sulla della pagina.
La suddivisione del codice può essere eseguita utilizzando la sintassi dinamica import()
. Questo
sintassi, a differenza degli elementi <script>
che richiedono una determinata risorsa JavaScript
durante l'avvio, effettua una richiesta per una risorsa JavaScript in un secondo momento
durante il ciclo di vita della pagina.
document.querySelectorAll('#myForm input').addEventListener('blur', async () => {
// Get the form validation named export from the module through destructuring:
const { validateForm } = await import('/validate-form.mjs');
// Validate the form:
validateForm();
}, { once: true });
Nello snippet JavaScript precedente, il modulo validate-form.mjs
è
scaricato, analizzato ed eseguito solo quando un utente sfoca uno qualsiasi dei moduli
<input>
campi. In questo caso, la risorsa JavaScript responsabile
la logica di convalida del modulo è coinvolta nella pagina solo quando
venga utilizzata con maggiore probabilità.
I bundleer JavaScript come webpack, Parcel, Rollup ed esbuild possono essere
configurati per suddividere bundle JavaScript in blocchi più piccoli
rileva una chiamata import()
dinamica nel codice sorgente. La maggior parte di questi strumenti
questa modalità è automatica, ma in particolare per esbuild
devi attivare questa funzionalità
e ottimizzazione.
Note utili sulla suddivisione del codice
Sebbene la suddivisione del codice sia un metodo efficace per ridurre la contesa del thread principale durante il caricamento iniziale della pagina, conviene tenere a mente alcuni aspetti se decidi per controllare il codice sorgente JavaScript per scoprire opportunità di suddivisione del codice.
Se puoi, utilizza un bundler
È pratica comune per gli sviluppatori utilizzare i moduli JavaScript durante il processo di sviluppo. È un eccellente miglioramento dell'esperienza degli sviluppatori che migliora la leggibilità e la manutenibilità del codice. Tuttavia, ci sono alcune caratteristiche di rendimento non ottimali che possono derivare dalla spedizione di JavaScript dei moduli in produzione.
Cosa più importante, dovresti utilizzare un bundler per elaborare e ottimizzare l'origine il codice, inclusi i moduli che intendi suddividere. I bundleer sono molto efficaci non solo applicando ottimizzazioni al codice sorgente JavaScript, ma abbastanza efficace nel bilanciare considerazioni sulle prestazioni, come le dimensioni dei pacchetti, rispetto al rapporto di compressione. L'efficacia della compressione aumenta con le dimensioni del bundle, ma i bundler cercano anche di fare in modo che i bundle non siano così grandi per attività lunghe grazie alla valutazione degli script.
I bundler evitano inoltre il problema di spedire un numero elevato di moduli non raggruppati
sulla rete. Le architetture che utilizzano i moduli JavaScript tendono ad avere
moduli complessi. Quando le strutture di moduli non sono raggruppate, ogni modulo rappresenta un
richiesta HTTP separata e l'interattività nella tua app web potrebbe subire ritardi
non raggruppano i moduli. Sebbene sia possibile utilizzare
Suggerimento di <link rel="modulepreload">
risorsa per caricare alberi di moduli di grandi dimensioni il prima possibile
il più possibile, i bundle JavaScript sono comunque preferibili
da un rendimento di caricamento
punto di vista.
Non disabilitare inavvertitamente la compilazione dei flussi di dati
Il motore JavaScript V8 di Chromium offre una serie di ottimizzazioni pronte all'uso per garantire che il codice JavaScript di produzione venga caricato nel modo più efficiente possibile. Una di queste ottimizzazioni è nota come compilazione di flussi di dati, che, come la analisi incrementale dell'HTML trasmesso al browser: compila blocchi di flussi di dati trasmessi in streaming JavaScript non appena arrivano dalla rete.
Esistono un paio di modi per assicurarti che la compilation streaming venga eseguita sul tuo app web in Chromium:
- Trasforma il tuo codice di produzione per evitare di utilizzare i moduli JavaScript. Costruttori può trasformare il codice sorgente JavaScript in base a una destinazione di compilazione e il target è spesso specifico per un dato ambiente. V8 applicherà lo streaming una compilazione in qualsiasi codice JavaScript che non utilizzi moduli e puoi configurare il bundler per trasformare il codice del modulo JavaScript in una sintassi che non utilizza i moduli JavaScript e le relative funzionalità.
- Se vuoi distribuire i moduli JavaScript in produzione, utilizza la
.mjs
. Indipendentemente dal fatto che il codice JavaScript di produzione utilizzi o meno i moduli, nessun tipo di contenuto speciale per JavaScript che utilizza moduli piuttosto che JavaScript ciò non avviene. Per quanto riguarda V8, disattivi effettivamente lo streaming quando invii i moduli JavaScript in produzione utilizzando l'.js
. Se utilizzi l'estensione.mjs
per i moduli JavaScript, V8 può assicura che la compilazione di flussi di dati per il codice JavaScript basato su moduli non funzionante.
Non lasciarti dissuadere da queste considerazioni dall'uso della suddivisione del codice. Codice la suddivisione è un modo efficace per ridurre i payload JavaScript iniziali agli utenti, ma utilizzando un bundler e sapendo come preservare i flussi di dati di compilazione, puoi assicurarti che il codice JavaScript di produzione sia più velocemente possibile per gli utenti.
Demo importazione dinamica
webpack
webpack viene fornito con un plug-in denominato SplitChunksPlugin
, che consente di
per configurare la modalità di suddivisione dei file JavaScript da parte del bundler. Webpack riconosce sia il modello
istruzioni import()
dinamiche e import
statiche. Il comportamento di
Puoi modificare SplitChunksPlugin
specificando l'opzione chunks
nel relativo
configurazione:
chunks: async
è il valore predefinito e si riferisce alle chiamateimport()
dinamiche.chunks: initial
si riferisce alle chiamateimport
statiche.chunks: all
copre sia le importazioniimport()
dinamiche sia quelle statiche, consentendoti per condividere i chunk traasync
einitial
importazioni.
Per impostazione predefinita, ogni volta che webpack rileva un'istruzione import()
dinamica. questo elemento
crea un blocco separato per quel modulo:
/* main.js */
// An application-specific chunk required during the initial page load:
import myFunction from './my-function.js';
myFunction('Hello world!');
// If a specific condition is met, a separate chunk is downloaded on demand,
// rather than being bundled with the initial chunk:
if (condition) {
// Assumes top-level await is available. More info:
// https://v8.dev/features/top-level-await
await import('/form-validation.js');
}
La configurazione Webpack predefinita per lo snippet di codice precedente genera due in blocchi separati:
- Il blocco
main.js
, che il webpack viene classificato come bloccoinitial
, che include i modulimain.js
e./my-function.js
. - Il blocco
async
, che include soloform-validation.js
(contenente un hash del file nel nome della risorsa, se configurato). Questo blocco viene scaricato solo se e quandocondition
è veramente.
Questa configurazione consente di rimandare il caricamento del blocco form-validation.js
fino a quando
in realtà è necessario. Questo può migliorare la reattività al caricamento riducendo lo script
di valutazione durante il caricamento iniziale della pagina. Download e valutazione degli script
del blocco form-validation.js
si verifica quando è soddisfatta una condizione specificata,
in questo caso viene scaricato il modulo importato dinamicamente. Un esempio potrebbe essere un
condizione in cui un polyfill viene scaricato solo per un determinato browser o, come
Nell'esempio precedente, il modulo importato è necessario per l'interazione dell'utente.
Al contrario, la modifica della configurazione SplitChunksPlugin
per specificare
chunks: initial
assicura che il codice venga suddiviso solo nei blocchi iniziali. Si tratta di
come quelli importati in modo statico o elencati nel file entry
proprietà privata. Osservando l'esempio precedente, il blocco risultante sarebbe una
combinazione di form-validation.js
e main.js
in un unico file di script,
con un conseguente peggioramento potenzialmente delle prestazioni
del caricamento iniziale della pagina.
Le opzioni per SplitChunksPlugin
possono anche essere configurate per separare i casi più grandi
in più script più piccoli, ad esempio utilizzando l'opzione maxSize
indica a webpack di suddividere i blocchi in file separati se superano quelli
specificato da maxSize
. La suddivisione di file di script di grandi dimensioni in file più piccoli consente
migliorare la reattività al carico, come in alcuni casi la valutazione degli script che fa un uso intensivo della CPU
Il lavoro è diviso in attività più piccole, che hanno meno probabilità di bloccare le
thread per periodi di tempo più lunghi.
Inoltre, generare file JavaScript di dimensioni maggiori significa anche che gli script più probabilità di subire l'annullamento della convalida della cache. Ad esempio, se spedisci un modello script di grandi dimensioni con codice sia del framework sia dell'applicazione proprietaria, il bundle può essere invalidato se viene aggiornato solo il framework, ma nient'altro in la risorsa in bundle.
D'altra parte, file di script più piccoli aumentano la probabilità che un reso il visitatore recupera le risorse dalla cache, velocizzando così il caricamento delle pagine visite ripetute. Tuttavia, i file più piccoli traggono meno vantaggio dalla compressione rispetto a quelli più grandi e possono aumentare il tempo di round trip della rete nei caricamenti delle pagine con un cache del browser. Si deve prestare attenzione a trovare un equilibrio tra la memorizzazione nella cache efficienza, efficacia della compressione e tempi di valutazione degli script.
demo webpack
demo di webpack SplitChunksPlugin
.
Verifica le tue conoscenze
Tipo di istruzione import
utilizzato durante l'esecuzione del codice
la suddivisione?
import
.import()
dinamica.
Quale tipo di istruzione import
deve trovarsi nella parte superiore
di un modulo JavaScript e in nessun'altra posizione?
import
.import()
dinamica.
Quando utilizzi SplitChunksPlugin
in webpack, qual è
differenza tra un blocco async
e un
initial
blocco?
async
di blocchi vengono caricati utilizzando il file statico import
e initial
chunk vengono caricati utilizzando
import()
.
async
chunk vengono caricati utilizzando la funzionalità dinamica import()
e initial
chunk vengono caricati utilizzando
import
.
A seguire: caricamento lento di immagini ed elementi <iframe>
Sebbene tenda a essere un tipo di risorsa piuttosto costoso, JavaScript non è
l'unico tipo di risorsa
di cui puoi rimandare il caricamento. Immagine ed elementi <iframe>
sono risorse potenzialmente costose. Analogamente a JavaScript,
può rimandare il caricamento delle immagini e degli elementi <iframe>
tramite caricamento lento
loro, come spiegato nel prossimo modulo di questo corso.