Webpack viene definito dai suoi stessi sviluppatori come uno static module bundler per applicazioni JavaScript. Il suo scopo è quindi quello di creare un pacchetto di assets utilizzabile direttamente nel browser a partire da un insieme di file sorgenti strutturati su diversi file e con schemi di dipendenze complessi.
Concetti fondamentali dei Webpack
Per comprendere al meglio il funzionamento di Webpack, è necessario comprendere alcuni concetti chiave.
- Un entry point rappresenta il punto di partenza da utilizzare durante il processo di building. Di default l'entry point è src/index.js.
- L'output rappresenta l'opposto dell'entry point, ovvero la cartella dove verrà generato il pacchetto di risorse utilizzabile dal browser. Di default la cartella di output è dist.
- I loaders sono il vero cuore di webpack; essi rappresentano i vari engine che permetteranno di generare il pacchetto finale. Di default Webpack utilizza solamente un loader per i file .js ma tramite questa opzione abbiamo la possibilità di definirne altri (per esempio per i file .sass o .less).
- I plugin sono simili ai loaders ma si occupano di aspetti più generali del processo di build mentre i loaders sono dedicati a determinati tipi di file: possiamo per esempio avere plugin di reportistica o che autoinstallano le dipendenze richieste dai sorgenti.
- Il mode rappresenta l'ambiente sul quale webpack viene lanciato. Di default esso è valorizzato come
production
.
Installazione dei Webpack
Webpack è un pacchetto Node tradizionale, installabile quindi tramite npm. È possibile installarlo localmente all'interno di un singolo progetto (tramite npm install webpack
) o globalmente tra gli eseguibili di sistema (tramite npm -g install webpack
). Il nostro suggerimento è quello di installarlo sempre localmente e di utilizzarlo tramite gli npm scripts in modo da poter cambiare versioni tra i vari progetti senza rischi di retrocompatibilità.
La versione attuale è la 4.19.1.
Configurazione
Nonostante l'approccio convention over configuration e quindi la possibilità di eseguire la build senza particolari configurazioni, Webpack permette comunque di personalizzare qualsiasi aspetto.
All'avvio, Webpack cerca nella root di progetto un file chiamato webpack.config.js. Nel caso sia presente, verrà utilizzato come file di configurazione. È comunque possibile modificare il nome del file tramite l'opzione --config
.
Il file di configurazione webpack.config.js è a tutti fatti un modulo NodeJS standard il cui compito è esportare un oggetto di configurazione.
Guardiamo un esempio di configurazione, basandoci sui concetti chiave evidenziati prima:
module.exports = {
mode: 'production',
entry: './src/app.js',
output: {
path: require('path').resolve(__dirname, 'build'),
filename: 'app.js'
},
module: [{
test: /\.scss$/,
use: [{
loader: 'sass-loader'
}]
}]
}
In questo semplice esempio abbiamo impostato un processo di build di produzione, il cui entry point si trova in src/app.js e l'output verrà generato dentro la folder build (il path deve sempre essere assoluto, per questo è stato utilizzato il modulo path). È stato inoltre incluso un loader custom, chiamato sass-loader
in grado di compilare i file .scss.
Le opzioni disponibili in webpack.config.js sono molte. Chi fosse interessato ad approfondire tutte le opzioni, anche quelle non approfondite in questo articolo, potrà consultare la documentazione ufficiale.
Gestione assets
Una delle principali differenze tra Webpack e i suoi avi (grunt e gulp in primis) è la possibilità di considerare tutti gli assets come dipendenze.
Nell'approccio "tradizionale", ogni tipologia di asset (Javascript, fogli di stile, immagini, font...) era gestita autonomamente: si scaricavano pacchetti dedicati all'uglify del codice, preprocessori CSS, ottimizzatori di immagini e si configuravano diversi "task" dedicati a ciasun aspetto dell'applicazione.
In Webpack, invece, tutto diventa dipendenza di qualcos'altro. Nell'entry point vengono incluse tutte le dipendenze: file CSS, immagini, font... Sarà compito di Webpack, grazie ai tanti loader disponibili, gestire le varie tipologie di file. Approfondiamo questo concetto con un esempio.
Progetto di esempio
In questo capitolo creeremo passo passo un nuovo progetto utilizzando Webpack come compositore di dipendenze.
Per prima cosa inizializziamo il progetto con npm e scarichiamo le prime dipendenze:
mkdir webpack-demo
cd webpack-demo
npm init
npm install webpack webpack-cli
Creiamo il nostro entry point, per ora vuoto, rispettando la convenzione src/index.js e aggiungiamo un custom script all'interno del nostro package.json:
"scripts": {
"webpack": "./node_modules/webpack/bin/webpack.js"
}
Se tutto è stato eseguito correttamente, lanciando lo script con npm run webpack
dovremmo riuscire (oltre a visualizzare un warning perchè manca il mode) a generare un file dist/main.js con un po' di codice Javascript minificato. Il contenuto del file è una sorta di codice di default di Webpack, quello che serve per gestire l'inclusione a runtime delle varie dipendenze.
Scarichiamo altre dipendenze:
npm install jquery popper.js bootstrap
Creiamo il file di configurazione di webpack in modo da evitare warning e modifichiamo il nostro index.js:
module.exports = {
mode: 'development'
};
window.jQuery = require('jquery');
require('bootstrap');
Lanciando nuovamente npm run webpack
, ora dovremmo trovare un file non più minimizzato (grazie a mode: development
) e più corposo, in quanto include anche jQuery e Boostrap.
Prima di proseguire, introduciamo un altro concetto, fondamentale per ottimizzare i tempi di sviluppo: il dev-server, ovvero la possibilità di avere un server web dedicato alla nostra applicazione e che procede autonomamente con le build del codice. Scarichiamo la dipendenza, aggiungiamo lo script in package.json e modifichiamo il file webpack.config.js:
npm install --save-dev webpack-dev-server
[...]
"webpack-dev-server": "./node_modules/webpack-dev-server/bin/webpack-dev-server.js"
[...]
devServer: {
contentBase: require('path').join(__dirname, 'dist'),
compress: true,
port: 9000
}
Ora lanciando npm run webpack-dev-server
, avviamo un server web sulla porta 9000 e un watcher che ad ogni modifica di un file rilancierà lo script di build.
Includiamo ora il CSS di Boostrap. Per poterlo fare abbiamo bisogno di due loader dedicati:
npm install css-loader style-loader
Modifichiamo quindi il nostro webpack.config.js per istruire webpack che i nuovi moduli devono occuparsi dei file con estensione .css.
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}]
},
Creiamo inoltre un file index.html all'interno della nostra cartella dist:
<!doctype html>
<html>
<head>
<title>Webpack demo</title>
</head>
<body>
<h1>Hello from Webpack and Bootstrap!</h1>
<script src="main.js"></script>
</body>
</html>
Se tutto corretto, dovremmo visualizzare il titolo della pagina formattato con lo stile di default di Bootstrap.
Come ultimo passo di questa mini guida, includeremo l'iconset di FontAwesome all'interno del nostro piccolo progetto. Installiamo le dipendenze richieste: il set di icone e un nuovo loader per Webpack.
npm install file-loader font-awesome
Configuriamo poi il loader e includiamo la dipendenza all'interno del nostro entry point:
module: {
rules: [{
test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
use: 'file-loader'
}]
},
require('font-awesome/css/font-awesome.css');
Ora lanciando il comando webpack (o tramite il webpack-dev-server), e all'interno della cartella dist dovremmo trovare nuovi file, ovvero i webfont appena inclusi. Webpack li ha rinominati con degli hash, ma questo non è un problema, perchè internamente si è occupato anche di modificare il frammento CSS che li include. Per fare una prova basta aggiungere un'icona all'interno del nostro file HTML:
<h2>
<i class="fa fa-android"></i>
<i class="fa fa-apple"></i>
</h2>
Se tutto è corretto, dovremmo vedere due icone: il robot di Android e la mela mordicchiata di Apple.
I sorgenti di questo progetto di esempio sono disponibili a questo link.
Conclusioni
Webpack è uno strumento molto complesso, ed in questo articolo abbiamo scalfito solamente una parte di tale complessità, che però permette di imparare i concetti chiave che permettono poi di approfondire le aree richieste.
La documentazione di Webpack è molto ben fatta, ma anche particolarmente ampia, e per questo può risultare dispersiva: il consiglio è quindi quello di approcciarla a piccoli passi, dedicando tempo solo alle sezioni che effettivamente servono.