JavaScript Moduli: import ed export con ricaricamento dinamico e code splitting

by Anastasia P.
SHARE
JavaScript Moduli: import ed export con ricaricamento dinamico e code splitting
© Guida-HTML5.it

Introduzione

I moduli JavaScript sono uno degli strumenti più importanti per scrivere codice moderno, ordinato e facile da mantenere. Con import ed export possiamo dividere un’applicazione in piccoli file indipendenti, riutilizzare funzioni e isolare responsabilità in modo pulito.

In questo tutorial ci concentriamo su un sotto-argomento molto pratico e spesso sottovalutato: il caricamento dinamico dei moduli con import(), utile per migliorare le prestazioni, caricare codice solo quando serve e implementare tecniche di lazy loading e code splitting.

Questo approccio è particolarmente utile in applicazioni reali: per esempio quando vuoi caricare un editor avanzato solo se l’utente apre un pannello, oppure importare un modulo di validazione solo al momento dell’invio di un form. In questi casi non conviene includere tutto nel bundle iniziale.

Codice completo

// file: mathUtils.js
export function add(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

export const PI = 3.14159;
// file: reportGenerator.js
export function generateReport(data) {
  return {
    total: data.length,
    items: data.map(item => `- ${item}`)
  };
}
// file: app.js

import { add, PI } from ´./mathUtils.js´;

const button = document.querySelector(´#load-report´);
const output = document.querySelector(´#output´);

console.log(´Somma:´, add(2, 3));
console.log(´PI:´, PI);

button.addEventListener(´click´, async () => {
  output.textContent = ´Caricamento modulo in corso...´;

  try {
    // Import dinamico: il modulo viene caricato solo quando serve
    const module = await import(´./reportGenerator.js´);

    const report = module.generateReport([´HTML´, ´CSS´, ´JavaScript´]);
    output.innerHTML = `
      <h3>Report generato</h3>
      <p>Totale elementi: ${report.total}</p>
      <pre>${report.items.join(´n´)}</pre>
    `;
  } catch (error) {
    output.textContent = ´Errore durante il caricamento del report.´;
    console.error(´Impossibile caricare il modulo:´, error);
  }
});
<!-- file: index.html -->
<button id="load-report">Carica report</button>
<div id="output"></div>

<script type="module" src="./app.js"></script>

Spiegazione

Nel codice abbiamo due tipi di importazione:

  • Import statico con import { add, PI } from ´./mathUtils.js´: il modulo viene caricato subito all’avvio dello script.
  • Import dinamico con await import(´./reportGenerator.js´): il modulo viene caricato solo quando l’utente clicca il pulsante.

1. Import statico

Gli import statici sono perfetti per le dipendenze sempre necessarie. Nel nostro esempio, le funzioni matematiche add e la costante PI servono fin da subito, quindi ha senso importarle subito.

2. Import dinamico con import()

La funzione import() non si usa come una dichiarazione normale, ma come una funzione che restituisce una Promise. Per questo possiamo usare await dentro una funzione async.

Il vantaggio principale è che il codice del modulo non viene incluso nel caricamento iniziale della pagina, ma scaricato solo al momento dell’uso. Questo riduce il peso iniziale e può migliorare l’esperienza utente, soprattutto su dispositivi lenti o reti instabili.

3. Accesso ai membri esportati

Quando importiamo dinamicamente un modulo, otteniamo un oggetto che contiene tutte le esportazioni del file. Nel nostro esempio:

  • module.generateReport è la funzione esportata da reportGenerator.js

Questo è diverso dall’import statico nominato, dove possiamo destrutturare direttamente i simboli importati.

4. Gestione degli errori

Il blocco try/catch è fondamentale. Un import dinamico può fallire se il file non esiste, se il percorso è errato o se c’è un problema di rete in un contesto browser. Gestire l’errore evita che l’applicazione si blocchi in modo silenzioso.

5. Caso d’uso reale: funzionalità opzionali

Immagina un’applicazione con molte funzionalità avanzate: editor visuale, grafici, esportazione PDF, strumenti di analisi. Caricare tutto all’inizio sarebbe inefficiente. Con import() puoi suddividere il codice in moduli caricati solo quando l’utente li richiede davvero.

Best practice

  • Usa import statici per le dipendenze core: tutto ciò che serve sempre all’applicazione dovrebbe essere importato normalmente.
  • Usa import dinamici per funzionalità opzionali: moduli pesanti o usati raramente sono candidati ideali per import().
  • Gestisci sempre gli errori: un fallback chiaro migliora robustezza e debug.
  • Evita import dinamici inutili: non trasformare ogni import in dinamico; il guadagno deve essere reale.
  • Organizza i moduli in modo coerente: nomi chiari, responsabilità singole e percorsi puliti facilitano la manutenzione.
  • Ricorda che import() restituisce una Promise: pianifica il flusso asincrono correttamente.

Riepilogo

In questo tutorial abbiamo visto come import ed export non servano solo a separare il codice in file diversi, ma anche a ottimizzare il caricamento dell’applicazione. Il punto centrale è l’uso di import(), una tecnica moderna che permette di caricare moduli solo quando servono.

Questo approccio è utile per migliorare le prestazioni, ridurre il bundle iniziale e rendere il codice più scalabile. In pratica, gli import statici rimangono la scelta migliore per le dipendenze fondamentali, mentre gli import dinamici sono perfetti per funzionalità secondarie o costose da caricare.

Approfondisci con risorse ufficiali

  • MDN Web Docs - JavaScript modules: documentazione completa sui moduli ECMAScript.
  • MDN Web Docs - import(): riferimento ufficiale per l’import dinamico.
  • ECMAScript Language Specification: specifica tecnica del linguaggio JavaScript.
  • Node.js Documentation - ECMAScript modules: utile se lavori anche in ambiente server-side.

SHARE