Introduzione ai Moduli JavaScript
I moduli JavaScript aiutano a gestire progetti di grandi dimensioni suddividendo il codice in parti separate e riutilizzabili.
I moduli JavaScript ti aiutano a gestire progetti di grandi dimensioni suddividendo il codice in parti separate e riutilizzabili. Questa guida approfondisce come usare i moduli JavaScript in modo efficace, migliorando le tue competenze di programmazione e l'organizzazione del progetto.
Questa pagina tratta cosa sia un modulo, la sintassi import/export, la differenza tra export nominati e default, come funziona lo scope dei moduli, i sistemi di moduli più vecchi che incontrerai ancora (CommonJS e AMD) e un esempio completo e funzionante che puoi eseguire.
Comprendere i Moduli JavaScript
Un modulo è semplicemente un file. Ogni file ha il proprio scope privato: le variabili e le funzioni dichiarate all'interno di un modulo non sono visibili all'esterno a meno che non vengano esplicitamente esportate con export. Per utilizzare qualcosa da un altro modulo, si usa import. Questo è il fondamento di ogni altro concetto trattato in questa pagina.
I moduli introducono anche due comportamenti automatici da conoscere:
- La modalità strict è sempre attiva. Ogni modulo viene eseguito come se iniziasse con
'use strict', quindi le variabili globali accidentali generano unReferenceErrorinvece di creare silenziosamente una variabile. thisal livello superiore èundefined, non l'oggetto globale. Questo è uno dei modi più semplici per capire se il codice viene eseguito come modulo.
Di seguito vengono illustrate la sintassi di base e l'utilizzo dei moduli.
Introduzione alla Sintassi dei Moduli
I moduli utilizzano le istruzioni import e export per condividere codice tra file.
Esempio:
// Exporting functions
export const add = (a, b) => a + b;
export function multiply(a, b) {
return a * b;
}
// Importing them in another file
import { add, multiply } from './mathFunctions.js';L'istruzione export ti consente di rendere disponibili parti del tuo modulo ad altri file. L'istruzione import ti consente di importare quelle parti dove ne hai bisogno.
Usa nomi chiari e descrittivi per i tuoi moduli e funzioni. Questo rende il codice più facile da leggere e mantenere.
Export Default vs Export Nominati
Puoi usare export default o export nominati per condividere parti diverse del tuo codice. Gli export nominati condividono più funzionalità per nome, mentre gli export default condividono una singola funzionalità senza specificare un nome.
Esempio:
// mathFunctions.js
export default function subtract(a, b) {
return a - b;
}
// Using the default export
import subtract from './mathFunctions.js';La parola chiave default viene usata per esportare una singola funzione o variabile. Quando si importa un export default, è possibile usare qualsiasi nome per riferirsi ad esso.
Gli export nominati sono adatti per le funzioni di utilità, mentre gli export default sono utili per la funzionalità principale di un file, come un componente React o una classe.
Un file può combinare un export default con un qualsiasi numero di export nominati. Si importano insieme, prima il default e poi quelli nominati tra parentesi graffe:
// mathFunctions.js
export const add = (a, b) => a + b; // named export
export default function subtract(a, b) { // default export
return a - b;
}
// app.js
import subtract, { add } from './mathFunctions.js';Alcune regole da ricordare:
- Un modulo può avere solo un export default, ma molti export nominati.
- Gli import nominati devono usare il nome esatto esportato (o rinominarlo con
as:import { add as sum } from './mathFunctions.js'). - Un import default può usare qualsiasi nome desiderato, perché il default non ha un nome fisso.
Ridenominazione e Re-esportazione
Puoi rinominare in ingresso o in uscita con as e riesportare da un altro modulo per creare un unico punto di accesso (un file "barrel"):
// utils.js
export { add as sum } from './mathFunctions.js';
export { default as subtract } from './mathFunctions.js';
// app.js
import { sum, subtract } from './utils.js';Sfruttare i Moduli per un Codice Pulito
L'uso dei moduli può rendere il codice più facile da gestire, soprattutto man mano che i progetti crescono.
Struttura delle Directory
Una buona organizzazione delle cartelle aiuta a mantenere il codice ordinato.
Esempio:
/src
/components
/helpers
/models
/services
index.jsGestione delle Dipendenze
Gestire le dipendenze significa assicurarsi che i file di codice funzionino correttamente insieme.
Esempio:
// Webpack configuration for bundling modules
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' }
]
}
};Questa configurazione dice a Webpack di partire da index.js, raggruppare quest'ultimo e tutte le sue dipendenze in bundle.js e collocare il risultato nella cartella dist.
Nota: i file di configurazione di Webpack utilizzano tradizionalmente la sintassi CommonJS (module.exports), anche nei progetti che fanno largo uso di ES6.
Tecniche Avanzate sui Moduli
L'utilizzo di funzionalità avanzate dei moduli può rendere il codice più efficiente e facile da gestire.
Import Dinamici
Gli import dinamici ti consentono di caricare il codice solo quando necessario, il che può velocizzare l'applicazione.
Esempio:
// Dynamically importing a module
// Assuming a button element exists
button.onclick = () => {
import('./messageModule.js')
.then(module => {
module.showMessage('Dynamic Import Executed!');
});
};Questo codice carica un modulo solo quando viene fatto clic su un pulsante, riducendo così i tempi di caricamento iniziale.
Usa gli import dinamici per le parti dell'applicazione non immediatamente necessarie, come funzionalità aggiuntive accessibili in un secondo momento.
import() restituisce una promise, quindi puoi usarla anche con async/await:
async function loadFeature() {
const module = await import('./messageModule.js');
module.showMessage('Loaded on demand');
}Consulta Import Dinamici per un approfondimento.
Comunicazione Tra Moduli
I moduli dovrebbero essere autonomi, ma possono comunicare attraverso risorse condivise.
Esempio:
// stateManager.js
export let state = { count: 0 };
// counter.js
import { state } from './stateManager.js';
state.count++;Questo codice mostra due moduli che condividono un object di stato. Poiché i moduli JavaScript vengono memorizzati nella cache come singleton, entrambi i file fanno riferimento allo stesso object in memoria. Quando un modulo modifica lo stato, l'altro vede il cambiamento.
Nota: questo esempio modifica direttamente l'object condiviso. Per le applicazioni in produzione, considera l'utilizzo di una libreria di gestione dello stato o di un pattern reattivo per gestire gli aggiornamenti in modo prevedibile.
Comprendere i Sistemi di Moduli e il Loro Utilizzo Oggi
Nello sviluppo web moderno, comprendere i vari sistemi di moduli è essenziale:
- CommonJS: Usato principalmente in Node.js per il codice lato server.
- AMD (Asynchronous Module Definition): Usato per il caricamento asincrono dei moduli, adatto ai browser.
- Moduli ES6: Lo standard nello sviluppo web moderno, che supporta sia il caricamento sincrono che asincrono.
Esempi per Ciascun Sistema di Moduli
Per illustrare questi concetti in JavaScript, includeremo frammenti di codice per ogni tipo di modulo accanto all'esempio ES6 esistente.
Esempio CommonJS:
// mathFunctions.js
exports.add = function(a, b) {
return a + b;
};
// app.js
const math = require('./mathFunctions.js');
console.log(math.add(5, 3));Spiegazione per CommonJS: In questo esempio, usiamo exports per rendere disponibile la funzione add al di fuori del file. Poi, in un altro file, usiamo require per importare la funzione add e poterla utilizzare. Questo sistema è comunemente usato in Node.js.
Esempio AMD:
// mathFunctions.js
define([], function() {
return {
add: function(a, b) {
return a + b;
}
};
});
// app.js
require(['mathFunctions'], function(math) {
console.log(math.add(5, 3));
});Spiegazione per AMD: Questo esempio usa define per dichiarare un modulo senza dipendenze e restituisce un object contenente la funzione add. require viene poi utilizzato per caricare il modulo in modo asincrono. Ciò è utile per caricare dinamicamente i moduli nel browser.
Esempio Moduli ES6:
// mathFunctions.js
export const add = (a, b) => a + b;
// app.js
import { add } from './mathFunctions.js';
console.log(add(5, 3));Spiegazione per i Moduli ES6: Qui usiamo export per rendere disponibile la funzione add e import per usarla in un altro file. Questo è lo standard moderno per la gestione dei moduli in JavaScript ed è supportato dalla maggior parte dei browser.
Abilitare i Moduli ES6 in Node.js
Quando si usano i moduli ES6 in Node.js, è possibile sfruttare la stessa sintassi import/export tipicamente usata nello sviluppo JavaScript front-end. Ciò consente una sintassi dei moduli coerente sia negli ambienti client che server.
Per usare la sintassi dei moduli ES6 in Node.js, devi assicurarti che il tuo ambiente la supporti. A partire da Node.js versione 14, i moduli ES6 sono stabili, e dalla versione 16 sono abilitati per impostazione predefinita quando è impostato "type": "module". Ecco come configurarlo:
Aggiorna package.json: nel file package.json del tuo progetto Node.js, aggiungi la seguente riga:
"type": "module"Questo indica a Node.js di trattare i file .js come moduli ES6 per impostazione predefinita.
Estensioni dei file: usa .js per i tuoi file modulo, oppure usa esplicitamente .mjs se preferisci. Node.js riconosce entrambi, ma se utilizzi l'impostazione "type": "module", si presuppone che .js sia un modulo ES6.
// mathFunctions.js
export function add(a, b) {
return a + b;
}
// app.js
import { add } from './mathFunctions.js';
console.log(add(5, 3)); // 8In Node.js ESM, gli import relativi richiedono l'estensione del file (./mathFunctions.js, non ./mathFunctions). Questo differisce da CommonJS, dove l'estensione è opzionale.
Errori Comuni
Alcuni comportamenti dei moduli mettono in difficoltà i principianti. Conoscerli in anticipo risparmia tempo nel debug:
- I percorsi relativi nel browser richiedono un'estensione.
import { add } from './math'funziona in molti bundler ma non funziona con il browser ESM nativo, dove è necessario scrivere'./math.js'. - I moduli vengono eseguiti una sola volta e vengono memorizzati nella cache. Indipendentemente da quanti file importano lo stesso modulo, il suo codice di primo livello viene eseguito una sola volta e ogni importatore condivide la stessa istanza. È questo che ha reso funzionale l'esempio con lo stato condiviso.
- Gli import sono binding in sola lettura, non copie. Non è possibile riassegnare un valore importato (
add = 5genera un errore). Si tratta di un collegamento live all'export, quindi se il modulo che esporta cambia il valore, i moduli che importano vedono il nuovo valore. import/exportdevono essere al livello superiore. Le istruzioniimporteexportstatiche non possono comparire all'interno di un bloccoifo di una funzione. Quando è necessario il caricamento condizionale, usa invece la funzione dinamicaimport().- Gli script modulo sono differiti. Un tag
<script type="module">nel browser non blocca il parsing; viene eseguito dopo che l'HTML è stato analizzato, in modo simile all'aggiunta didefer.
Per ulteriori informazioni sulla modalità strict sempre attiva, consulta Strict mode. Per il riferimento completo di export/import, consulta Export and Import.
Best Practice per l'Utilizzo dei Moduli JavaScript
- Semplicità: Usa nomi chiari e semplici per i tuoi file e gli export.
- Coerenza: Applica gli stessi pattern e strutture in tutto il progetto per rendere il codice prevedibile.
- Documentazione: Commenta il codice e documenta come usare i tuoi moduli.
- Ottimizzazione quando necessario: Rivedi e ottimizza regolarmente il codice man mano che il progetto cresce.
Esempio Completo
Di seguito è riportato un esempio completo che integra tutto ciò che hai imparato in questo articolo.
Struttura del Progetto:
/src
/math
- mathFunctions.js
- app.js
index.htmlmathFunctions.js:
export const add = (a, b) => a + b;
export default function subtract(a, b) {
return a - b;
}app.js:
import subtract, { add } from './math/mathFunctions.js';
document.getElementById('add').addEventListener('click', function() {
const result = add(5, 3);
document.getElementById('result').textContent = `Adding: ${result}`;
});
document.getElementById('subtract').addEventListener('click', function() {
const result = subtract(5, 3);
document.getElementById('result').textContent = `Subtracting: ${result}`;
});index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Module Example</title>
</head>
<body>
<button id="add">Add 5 + 3</button>
<button id="subtract">Subtract 5 - 3</button>
<div id="result"></div>
<script type="module" src="src/app.js"></script>
</body>
</html>Nota: i moduli ES richiedono un server di sviluppo locale (ad es. npx serve o Vite) per essere eseguiti nel browser a causa delle restrizioni CORS. L'apertura di index.html direttamente tramite file:// non funzionerà.
Questa configurazione utilizza una semplice pagina web con pulsanti per dimostrare l'addizione e la sottrazione di numeri usando funzioni importate. I risultati vengono visualizzati direttamente sulla pagina.
Spiegazione dell'Esempio
- mathFunctions.js: Questo file contiene due funzioni (
addesubtract) esportate come moduli.addè un export nominato, mentresubtractè un export default. - app.js: Questo file importa le funzioni da
mathFunctions.jse le associa agli eventi di clic sui pulsanti per eseguire calcoli quando l'utente interagisce con la pagina. - index.html: Il file HTML imposta l'interfaccia utente con pulsanti e un'area di visualizzazione per i risultati. Collega
app.jscome modulo.
Questo esempio completo dimostra come i moduli JavaScript possano essere strutturati e utilizzati all'interno di una vera applicazione.
Conclusione
I moduli JavaScript sono uno strumento potente per organizzare e mantenere applicazioni web su larga scala. Comprendendo e utilizzando correttamente i diversi sistemi di moduli, puoi migliorare la scalabilità e la manutenibilità del tuo progetto. Aggiornare regolarmente la tua conoscenza della sintassi dei moduli, delle best practice e delle tecniche avanzate garantirà che le tue competenze di sviluppo rimangano affilate e i tuoi progetti restino all'avanguardia.