Suddivisione del codice con importazioni dinamiche in Next.js

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

Milica Mihajlija
Milica Mihajlija

Cosa imparerai?

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

Suddivisione del codice in base a route e componenti

Per impostazione predefinita, Next.js suddivide il codice JavaScript in blocchi distinti 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 chunk associati agli altri percorsi. La suddivisione del codice in base ai route riduce al minimo la quantità di script che deve essere analizzata e compilata contemporaneamente, con tempi di caricamento delle pagine più rapidi.

Sebbene la suddivisione del codice in base alla route sia una buona impostazione predefinita, puoi ottimizzare ulteriormente il processo di caricamento con la suddivisione del codice a livello di componente. Se la tua app contiene componenti di grandi dimensioni, ti consigliamo di suddividerli in blocchi separati. In questo modo, tutti i componenti di grandi dimensioni che non sono critici o che vengono visualizzati solo in determinate interazioni con l'utente (ad esempio facendo clic su un pulsante) possono essere caricati in modo lazy.

Next.js supporta il import() dinamico, che consente di importare moduli JavaScript (inclusi i componenti React) in modo dinamico e di caricare ogni importazione come chunk 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. In Next.js, questi componenti vengono generati lato server (SSR) per impostazione predefinita.

Importazioni dinamiche in azione

Questo post include diverse versioni di un'app di esempio costituita da una semplice pagina con un pulsante. Quando fai clic sul pulsante, vedi un cucciolo carino. Man mano che scorri ogni versione dell'app, scoprirai in che modo le importazioni dinamiche sono diverse dalle importazioni statiche e come utilizzarle.

Nella prima versione dell'app, il cucciolo vive a 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 esegue il bundling dell'app, ispeziona la traccia di rete in DevTools:

  1. Per visualizzare l'anteprima del sito, premi Visualizza app, quindi premi A 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 Disattiva cache.

  5. Ricarica la pagina.

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

Scheda Rete di DevTools che mostra sei file JavaScript: index.js, app.js, webpack.js, main.js, 0.js e il file DLL (Dynamic Link Library).

Quando premi il pulsante Fammi clic, alla scheda Rete viene aggiunta solo la richiesta del file JPEG del cucciolo:

Scheda Rete di 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 nelle applicazioni reali spesso è un enorme miglioramento caricare componenti di grandi dimensioni solo quando necessario.

Ora dai un'occhiata a una seconda versione dell'app, in cui l'importazione statica è sostituita da un'importazione dinamica. Next.js include next/dynamic, che consente di utilizzare le importazioni dinamiche per qualsiasi componente in 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.

Al primo caricamento dell'app viene scaricato solo index.js. Questa volta è inferiore di 0,5 KB (da 37,9 KB a 37,4 KB) perché non include il codice per il componente Puppy:

La rete DevTools mostra gli stessi sei file JavaScript, tranne che il file index.js ora è 0,5 KB più piccolo.

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

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

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

Importazioni dinamiche con indicatore di caricamento personalizzato

Quando carichi le risorse in modo lazy, è buona norma fornire un indicatore di caricamento nel caso in cui si verifichino 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 premi A 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 Disattiva cache.

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

  6. Premi il pulsante Fai clic qui.

Ora, quando fai clic sul pulsante, il caricamento del componente richiede un po' di tempo e nel frattempo l'app visualizza il messaggio "Caricamento…".

Uno schermo scuro con il testo

Importazioni dinamiche senza SSR

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

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

Conclusione

Con il supporto delle importazioni dinamiche, Next.js 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 visualizzati lato server e puoi disattivare questa opzione ogni volta che è necessario.