Bundler è uno strumento di build che combina più moduli JavaScript in uno o più file per l'uso in un browser o in un'applicazione mobile. I bundler moderni (Webpack, Metro, Vite) non solo uniscono i file, ma eseguono anche trasposizione, minificazione e ottimizzazione delle risorse. Secondo Webpack Concepts (2026), una corretta configurazione del bundler riduce la dimensione della build del 40–60% senza perdere funzionalità.
Punti chiave
Bundler (raggruppatore di moduli) è uno strumento a riga di comando che prende un punto di ingresso — il file principale dell'applicazione — e attraversa ricorsivamente tutte le sue dipendenze, costruendo un grafo di moduli. In output, il bundler genera uno o più file che possono essere inclusi in una pagina HTML o eseguiti in un'applicazione mobile.
Per molto tempo, i browser non supportavano un sistema di moduli JavaScript a livello di piattaforma. Bundler ha risolto questo problema trasformando centinaia di import e require in un unico file. Oggi, anche con il supporto nativo dei moduli ES nei browser, i bundler svolgono attività aggiuntive: trasposizione di JSX e TypeScript, minificazione del codice, code splitting per il caricamento lazy e hot module replacement (HMR) per accelerare lo sviluppo.
Nello sviluppo mobile, React Native utilizza Metro come bundler predefinito, mentre Flutter utilizza il proprio sistema di build basato su Dart. La scelta del bundler influisce direttamente sulla velocità di sviluppo, sulla dimensione dell'applicazione e sulle prestazioni di runtime.
Il raggruppamento attraversa diverse fasi: analisi dei file di input, costruzione del grafo delle dipendenze, trasformazione dei moduli e generazione dei file di output. In ogni fase, il bundler applica loader per trasformare il codice sorgente e plugin per l'ottimizzazione.
Il bundler inizia dal punto di ingresso, legge il file e costruisce un AST (Albero Sintattico Astratto). Dall'AST vengono estratte tutte le dichiarazioni import e require. Per ogni import trovato, il bundler ripete il processo ricorsivamente finché tutte le dipendenze non sono raccolte in un unico grafo.
// Implementazione semplificata di un bundler
const fs = require('fs');
const path = require('path');
function buildGraph(entry) {
const content = fs.readFileSync(entry, 'utf-8');
const imports = content.match(/require\(['"](.+?)['"]\)/g);
const modulePath = path.resolve(path.dirname(entry), imports[0]);
return { entry, content, deps: [buildGraph(modulePath)] };
}
Dopo aver costruito il grafo, il bundler applica loader — trasformazioni che convertono i file in JavaScript: TypeScript → JS, SCSS → CSS, JSX → funzioni JSX. Poi i plugin eseguono trasformazioni aggiuntive: minificazione, incorporamento di immagini, generazione di service worker.
I bundler si dividono in tre generazioni: classici, specializzati e di nuova generazione basati su moduli ES. Ogni tipo ha la propria architettura e area di applicazione.
Webpack è il bundler più diffuso con un ecosistema enorme. Il suo principale vantaggio è la flessibilità: migliaia di loader e plugin coprono praticamente qualsiasi scenario di build. Parcel offre un approccio zero-config, rilevando automaticamente le trasformazioni necessarie in base alle estensioni dei file. Entrambi i bundler supportano code splitting e HMR.
Metro è stato creato specificamente per React Native e funziona solo con JavaScript e TypeScript. La sua architettura è ottimizzata per lo sviluppo mobile: supporto per inline requires, caricamento asincrono dei moduli e integrazione con il motore Hermes. Rollup è focalizzato su librerie e pacchetti npm, generando output di moduli ES pulito senza wrapper superflui.
La scelta del bundler dipende dalla piattaforma e dai requisiti del progetto. Webpack è versatile ma richiede una configurazione dettagliata. Metro è l'unica opzione per React Native. Vite offre l'esperienza di sviluppo più rapida grazie ai moduli ES nativi.
| Caratteristica | Webpack | Metro | Vite |
|---|---|---|---|
| Piattaforma | Web, universale | React Native | Web, universale |
| Velocità build dev | Media | Alta | Molto alta |
| Code splitting | Sì | Sì | Sì |
| HMR | Sì | Fast Refresh | HMR istantaneo |
| Ecosistema plugin | Enorme | Limitato | In crescita |
| Configurazione | Complessa | Semplice | Semplice |
| Supporto TypeScript | Tramite loader | Integrato | Integrato |
Vite utilizza esbuild per il pre-raggruppamento delle dipendenze e Rollup per le build di produzione, offrendo un miglioramento di velocità di 5–10 volte rispetto a Webpack in modalità sviluppo. Metro non supporta target web ma fornisce un'integrazione perfetta con il bridge React Native e i Turbo Modules.
La configurazione del bundler determina come vengono elaborati diversi tipi di file, dove viene generata la build e quali ottimizzazioni vengono applicate. Un file di configurazione tipico include il punto di ingresso, regole per i moduli, plugin e impostazioni di output.
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true,
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react'] } },
},
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
],
},
plugins: [new HtmlWebpackPlugin({ template: './public/index.html' })],
devServer: { port: 3000, hot: true },
};
Metro utilizza file separati metro.config.js e babel.config.js per la configurazione. A differenza di Webpack, la configurazione di Metro è significativamente più semplice poiché il bundler è specializzato per React Native e non richiede la configurazione di loader per stili o immagini.
Anche gli sviluppatori esperti incontrano errori di raggruppamento. Duplicate module è uno dei problemi più comuni, quando la stessa dipendenza appare più volte nel bundle a causa di incompatibilità di versione in package.json. Module not found si verifica quando il percorso di importazione è errato o il pacchetto non è installato.
Metro è limitato dalle dimensioni del bundle per i dispositivi mobili. Quando il limite di 2–5 MB viene superato, si verifica l'errore Unable to resolve module a causa della memoria insufficiente sul dispositivo. La soluzione è utilizzare inline requires e RAM bundles per il caricamento lazy dei moduli. Metro è anche sensibile ai symlink, quindi i monorepo richiedono una configurazione aggiuntiva di watchFolders.
Per Webpack, un problema tipico è Module parse failed, che si verifica quando un file ha un formato inaspettato o manca il loader necessario. Controllare l'estensione del file e aggiungere la regola appropriata in module.rules risolve il problema. L'uso di source-map-explorer aiuta a trovare moduli duplicati e dipendenze ridondanti nel bundle finale.
Le build lente sono uno dei problemi principali quando si lavora con i bundler. Per accelerare, utilizza la memorizzazione nella cache persistente, che salva i risultati della trasformazione dei moduli tra le esecuzioni. Webpack 5 supporta la cache di file tramite cache: { type: 'filesystem' }, Vite utilizza esbuild per il pre-raggruppamento e Metro si basa sulla cache dei moduli in memoria. Inoltre, specifica exclude per node_modules nei loader per evitare di rielaborare pacchetti già pronti.
Code splitting divide il bundle in chunk caricati su richiesta. Diversi bundler implementano questa strategia in modo diverso. Webpack supporta le importazioni dinamiche import(), creando chunk separati per ogni modulo. Metro utilizza inline requires per il caricamento differito. Vite divide automaticamente i vendor chunk e le importazioni dinamiche senza configurazione aggiuntiva. La scelta della strategia dipende dai requisiti del progetto: per le applicazioni mobili, sono preferibili bundle iniziali più piccoli con caricamento lazy.
Per il corretto funzionamento del code splitting in React Native, è necessaria una configurazione aggiuntiva: Metro deve essere configurato per chunk asincroni tramite inlineRequires. In Webpack e Vite, il code splitting funziona immediatamente quando si utilizza la sintassi dinamica import(), che crea automaticamente punti di divisione. Per la massima efficienza, combina il code splitting con suggerimenti di precaricamento tramite <link rel="preload"> per i chunk critici.
Domande frequenti
Per progetti semplici senza JSX, TypeScript o moduli CSS, puoi utilizzare i moduli ES nativi nel browser. Tuttavia, per le build di produzione, un bundler fornisce minificazione, tree shaking e code splitting, che sono critici per le prestazioni. Senza un bundler, è difficile mantenere un'architettura modulare in progetti grandi.
Per React Native — Metro (l'unica opzione supportata). Per applicazioni web — Vite per nuovi progetti (grazie alla velocità) o Webpack per progetti esistenti con un ricco ecosistema. Per librerie e pacchetti npm — Rollup, poiché genera output di moduli ES pulito.
Il bundler determina direttamente la dimensione del bundle JS finale. Webpack con tree shaking può ridurre la dimensione del 30–50%. Metro per React Native supporta il bytecode Hermes, che riduce la dimensione del 20–30% rispetto al JavaScript normale. Il code splitting permette di caricare moduli su richiesta, riducendo il bundle iniziale.
Per il web, il bundler genera codice compatibile con diversi browser e supporta CSS, immagini e font. Per le piattaforme mobili, Metro genera codice per un motore JavaScript (Hermes o JSC), non elabora CSS o HTML e ottimizza la build per le risorse limitate del dispositivo.
Sì, alcuni progetti utilizzano Webpack per la parte web e Metro per React Native — ad esempio, in monorepo con codebase condivisa. Tuttavia, ciò complica la configurazione CI/CD e richiede la sincronizzazione delle versioni delle dipendenze. Si consiglia di utilizzare un unico bundler per tutti i target, se possibile.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche