Suddivisione del codice con importazioni dinamiche in Next.js

Come velocizzare l'app Next.js con strategie di suddivisione del codice e caricamento intelligente.

Milica Mihajlija
Milica Mihajlija

Cosa imparerai a fare?

Questo post illustra i diversi tipi di suddivisione del codice e come utilizzare le importazioni dinamiche per velocizzare le app Next.js.

Suddivisione del codice basata su route e componenti

Per impostazione predefinita, Next.js suddivide il codice JavaScript in blocchi separati per ogni route. Quando gli utenti caricano la tua applicazione, Next.js invia solo il codice necessario per la route iniziale. Quando gli utenti navigano nell'applicazione, recuperano i blocchi associati alle altre route. La suddivisione del codice basata su route riduce al minimo la quantità di script che deve essere analizzato e compilato contemporaneamente, il che si traduce in tempi di caricamento della pagina più rapidi.

Anche se la suddivisione del codice in base alle route è un buon valore predefinito, puoi ottimizzare ulteriormente il processo di caricamento con la suddivisione del codice a livello di componente. Se nell'app sono presenti componenti di grandi dimensioni, è un'ottima idea suddividerli in blocchi separati. In questo modo, tutti i componenti di grandi dimensioni che non sono fondamentali o che vengono visualizzati solo in base a determinate interazioni dell'utente (ad esempio, il clic su un pulsante) possono essere caricati tramite caricamento lento.

Next.js supporta il formato import() dinamico, che consente di importare dinamicamente i moduli JavaScript (inclusi i componenti di React) e caricare ogni importazione come blocco separato. In questo modo, puoi suddividere il codice a livello di componente e controllare il caricamento delle risorse, in modo che gli utenti scarichino solo il codice necessario per la parte del sito che stanno visualizzando. Per impostazione predefinita, in Next.js questi componenti hanno il rendering lato server (SSR).

Funzionamento delle importazioni dinamiche

Questo post include diverse versioni di un'app di esempio costituita da una semplice pagina con un pulsante. Quando fai clic sul pulsante, hai la possibilità di vedere un adorabile cucciolo. Mentre ti sposti tra le varie versioni dell'app, vedrai le differenze tra le importazioni dinamiche e le importazioni statiche e come utilizzarle.

Nella prima versione dell'app, il cucciolo vive in components/Puppy.js. Per visualizzare il cucciolo nella pagina, l'app importa il componente Puppy in index.js con un'istruzione di importazione statica:

import Puppy from "../components/Puppy";

Per vedere come Next.js raggruppa l'app, esamina la traccia di rete in DevTools:

  1. Per visualizzare l'anteprima del sito, premi Visualizza app, quindi Schermo intero schermo intero.

  2. Premi "Control+Maiusc+J" (o "Comando+Opzione+J" su Mac) per aprire DevTools.

  3. Fai clic sulla scheda Rete.

  4. Seleziona la casella di controllo Disabilita cache.

  5. Ricarica la pagina.

Quando carichi la pagina, tutto il codice necessario, incluso il componente Puppy.js, viene aggregato in index.js:

Scheda Rete DevTools che mostra sei file JavaScript: index.js, app.js, webpack.js, main.js, 0.js e il file dll (libreria di link dinamici).

Quando premi il pulsante Click me, solo la richiesta relativa al JPEG cucciolo viene aggiunta alla scheda Network (Rete):

Scheda Rete DevTools dopo il clic sul pulsante, che mostra gli stessi sei file JavaScript e un'immagine.

Lo svantaggio di questo approccio è che anche se gli utenti non fanno clic sul pulsante per vedere il cucciolo, devono caricare il componente Puppy perché è incluso in index.js. In questo piccolo esempio non è un problema, ma per le applicazioni reali spesso è un enorme miglioramento caricare componenti di grandi dimensioni solo quando necessario.

Adesso vediamo una seconda versione dell'app, in cui l'importazione statica viene sostituita con un'importazione dinamica. Next.js include next/dynamic, il che consente di utilizzare le importazioni dinamiche per qualsiasi componente di Next:

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

Segui i passaggi del primo esempio per ispezionare la traccia di rete.

Quando carichi l'app per la prima volta, viene scaricata solo l'app index.js. Questa volta è più piccola di 0,5 kB (da 37,9 kB a 37,4 kB) perché non include il codice per il componente Puppy:

La rete DevTools che mostra gli stessi sei file JavaScript, ad eccezione del fatto che il file index.js è ora inferiore di 0,5 kB.

Il componente Puppy ora si trova in un blocco separato, 1.js, che viene caricato solo quando premi il pulsante:

Scheda Rete DevTools dopo il clic sul pulsante, che mostra il file 1.js aggiuntivo e l'immagine aggiunta alla fine dell'elenco dei file.

Nelle applicazioni reali, i componenti sono spesso molto più grandi e il caricamento lento può ridurre il payload JavaScript iniziale di centinaia di kilobyte.

Importazioni dinamiche con indicatore di caricamento personalizzato

Quando esegui il caricamento lento delle risorse, è buona norma fornire un indicatore di caricamento in caso di ritardi. In Next.js, puoi farlo fornendo un argomento aggiuntivo alla funzione dynamic():

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading: () => <p>Loading...</p>
});

Per vedere l'indicatore di caricamento in azione, simula una connessione di rete lenta in DevTools:

  1. Per visualizzare l'anteprima del sito, premi Visualizza app, quindi Schermo intero schermo intero.

  2. Premi "Control+Maiusc+J" (o "Comando+Opzione+J" su Mac) per aprire DevTools.

  3. Fai clic sulla scheda Rete.

  4. Seleziona la casella di controllo Disabilita cache.

  5. Nell'elenco a discesa Riduzione, seleziona 3G veloce.

  6. Premi il pulsante Click me (Fai clic qui).

Ora, quando fai clic sul pulsante, occorre un po' di tempo per caricare il componente e nell'app viene visualizzato il messaggio "Caricamento in corso...".

Una schermata scura con il testo

Importazioni dinamiche senza SSR

Se devi visualizzare un componente solo sul lato client (ad esempio un widget di chat), puoi farlo impostando l'opzione ssr su false:

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr: false,
});

Conclusione

Grazie al supporto delle importazioni dinamiche, Next.js ti offre la suddivisione del codice a livello di componente, che può ridurre al minimo i payload JavaScript e migliorare il tempo di caricamento dell'applicazione. Per impostazione predefinita, tutti i componenti vengono sottoposti a rendering lato server e puoi disabilitare questa opzione quando necessario.