JavaScript con suddivisione del codice

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 chiamate import() dinamiche.
  • chunks: initial si riferisce alle chiamate import statiche.
  • chunks: all copre sia le importazioni import() dinamiche sia quelle statiche, consentendoti per condividere i chunk tra async e initial 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 blocco initial, che include i moduli main.js e ./my-function.js.
  • Il blocco async, che include solo form-validation.js (contenente un hash del file nel nome della risorsa, se configurato). Questo blocco viene scaricato solo se e quando condition è 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() dinamica.
Statica import.

Quale tipo di istruzione import deve trovarsi nella parte superiore di un modulo JavaScript e in nessun'altra posizione?

import() dinamica.
Statica import.

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.