Grunt: un tool per l'ottimizzazione dei progetti Javascript
Nel corso dello sviluppo lato frontend di un'applicazione o di un sito web accade spesso di dover ripetere più volte, anche all'interno dello stesso progetto, alcune operazioni (come ad esempio l'ottimizzazione delle immagini, la concatenazione e la minificazione degli script, ecc.) necessarie per compilare la versione definitiva, a volte detta di produzione.
Grunt è uno strumento utilizzato dalla maggior parte degli sviluppatori per rendere veloci, precisi ed efficienti questi compiti. Benché si tratti di un software richiamato tramite linea di comando, il suo utilizzo è piuttosto semplice, e il piccolo sforzo richiesto per preparare i file di configurazione viene ripagato molto velocemente.
Grunt è un software costruito sulla base di Node.js, ma non c'è bisogno di padroneggiare quella tecnologia server per potersene avvantaggiare.
Installazione
Grunt e i suoi componenti aggiuntivi (li vedremo a breve) si installano facilmente tramite npm, il gestore di pacchetti incluso in Node.js. Possiamo installare Node tramite un classico wizard grafico presente sia per Windows che per Mac. Per Linux l'installazione si esegue tramite i più noti gestori di pacchetti (per una lista delle opzioni disponibili si può visitare questa pagina).
Al termine della procedura avremo installato npm, e sarà quindi sufficiente il comando
npm install -g grunt-cli
per avere a disposizione l'interfaccia testuale di Grunt per tutti i nostri progetti.Configurazione
Trattandosi di una tecnologia basata su Node.js, all'interno della cartella di lavoro dovrà essere presente un file denominato package.json nel quale vengono specificate alcune informazioni sul progetto e le dipendenze di cui abbiamo bisogno. Attenzione però: le dipendenze di cui si parla non sono quelle relative, ad esempio, alle librerie JavaScript che stiamo utilizzando nell'applicazione web(jQuery, Angular, ecc.), bensì quelle relative agli strumenti di sviluppo che Node.js può fornirci, come appunto Grunt.
Un modo molto semplice per iniziare è copiare questo esempio di base in package.json:
{
"name": "il-mio-progetto",
"version": "0.1.0"
}
A questo punto possiamo aggiungere Grunt alle dipendenze necessarie tramite il comando
npm install grunt -save-dev
Il file package.json viene automaticamente aggiornato includendo all'iterno della voce devDependencies anche l'utima versione stabile disponibile di Grunt. Ogni altro componente di cui potremo aver bisogno può essere aggiunto con lo stesso comando. Ad esempio:
npm install grunt-contrib-concat -save-dev
farà includere grunt-contrib-concat (un plugin di Grunt che useremo a breve) nelle dipendenze del progetto.
In seguito digitando a terminale
npm install
npm si occuperà di scaricare, in una sottocartella denominata node_modules, i componenti specificati in package.json. Abbiamo già installato globalmente l'interfaccia testuale di Grunt (grunt-cli), ma i suoi file eseguibili dovranno essere fisicamente collocati nello spazio di lavoro di ogni nuovo lavoro.
Allo stesso livello di package.json dovremo creare un file di configurazione denominato gruntfile.js dal quale Grunt leggerà i dati di configurazione essenziali per i plugin che intendiamo usare, e anche quali compiti devono essere eseguiti quando viene lanciato il comando "grunt".
Partiamo da questo esempio, che possiamo usare come struttura di base:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
//Configurazione per il plugin di concatenazione
concat: {
dist: {
src: [
'js/libs/jquery.js' , //jQuery deve essere caricato per primo
'js/libs/*.js', // Tutti i file con estensione .js presenti nella cartella libs
'js/script.js' // La nostra applicazione
],
dest: 'js/build/production.js', //File di destinazione
}
}
});
// Specifichiamo quali plugin usare
grunt.loadNpmTasks('grunt-contrib-concat');
//Specifichiamo quali compiti eseguire
grunt.registerTask('default', ['concat']);
};
Concatenare e minificare i file JavaScript
Prepariamo la struttura del nostro progetto, rispettando quella struttura proposta dal gruntfile del nostro esempio.
All'interno della cartella libs includeremo tutte le librerie che vogliamo utilizzare, ad esempio jQuery e jQuery UI, e nella cartella "js" il file script.js con la nostra applicazione. Eseguendo il comando grunt verrà creato il file production.js nella cartella "build" che avrà concatenato i file indicati nel parametro "src".
Con lo stesso meccanismo possiamo includere un altro plugin dedicato alla minificazione, prima includendolo nella cartella node_modules tramite il comando
npm install grunt-contrib-uglify --save-dev
e poi inserendo la configurazione nel gruntfile, che quindi ora si presenta così:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
//Configurazione per il plugin di concatenazione
concat: {
dist: {
src: [
'js/libs/jquery.js' ,
'js/libs/*.js',
'js/script.js'
],
dest: 'js/build/production.js',
}
},
uglify: {
build: {
src: 'js/build/production.js',
dest: 'js/build/production.min.js'
}
}
});
// Specifichiamo quali plugin usare
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
//Specifichiamo quali compiti eseguire al usando il comando "grunt"
grunt.registerTask('default', ['concat'], ['uglify']);
};
Lanciando grunt otterremo anche un file denominato production.min.js che è la versione minificata di production.js.
Automazione e LiveReload
I plugin disponibili per Grunt sono al momento più di 4000, e nella loro documentazione viene generalmente indicata la procedura necessaria per poterli utilizzare.
Fra quelli più famosi, oltre ai già menzionati contrib-concat e contrib-uglify, vale la pena ricordare contrib-watch, un componente che rende Grunt uno strumento ancora più potente.
Dopo aver installato il plugin con il comando
npm install grunt-contrib-watch -save-dev
e dopo aver incluso nel gruntfile la registrazione del plugin con
grunt.loadNpmTasks('grunt-contrib-watch');
possiamo scrivere, nella sua configurazione, queste semplici opzioni (occorre, come dovrebbe essere evidente, aver incluso precedentemente contrib-concat e contrib-uglify)
watch: {
scripts: {
files: ['js/*.js', 'css/*.css'], //Comprende la cartella degli script e quella dei fogli di stile
tasks: ['concat', 'uglify'],
options: {
spawn: false,
},
}
}
Adesso possiamo lanciare il tutto con il comando grunt-watch. Ogni qualvolta i file specificati nel parametro files (tutti i file JavaScript compresi nella cartella js e tutti i fogli di stile nella cartella css) vengono modificati il plugin si occupa di ripercorrere i compiti specificati dal parametro tasks ricreando il file production.js e la sua versione minificata.
Ancora più interessante notare come questo plugin possa combinarsi con il famoso componente LiveReload disponibile per Firefox, Safari e Chrome.
Abilitando l'opzione livereload sempre all'interno della sua configurazione, pochi istanti dopo aver operato un cambiamento potremo vedere il risultato (molto utile specialmente per i css) nel browser senza ricaricare la pagina.
In conclusione, il gruntfile risulterà il seguente:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
//Configurazione per il plugin di concatenazione
concat: {
dist: {
src: [
'js/libs/jquery.js' ,
'js/libs/*.js',
'js/script.js'
],
dest: 'js/build/production.js',
}
},
//Configurazione per il plugin di minificazione
uglify: {
build: {
src: 'js/build/production.js',
dest: 'js/build/production.min.js'
}
},
//Configurazione per il plugin di controllo
watch: {
options: {
livereload: true, //Collegamento a LiveReload
},
scripts: {
files: ['js/*.js', 'css/style.css'], //Comprende la cartella degli script e quella dei fogli di stile
tasks: ['concat', 'uglify'],
options: {
spawn: false,
},
}
}
});
// Specifichiamo quali plugin usare
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
//Specifichiamo quali compiti eseguire al usando il comando "grunt"
grunt.registerTask('default', ['concat'], ['uglify'], ['watch']);
};
Conclusioni
Grunt è indubbiamente uno strumento essenziale per uno sviluppatore professionista, ed è molto flessibile data la grande varietà di plugin disponibili (ottimizzazione delle immagini, compilazione di SASS, Less e CoffeeScript, compiti specifici relativi a framework di sviluppo e di test, ecc.) messi a punto dai creatori del software e dalla comunità che vi si è formata attorno.
Infine, Grunt può integrarsi perfettamente con altri potenti strumenti come Bower e Yeoman, che, interagendo fra loro, rendono il lavoro del programmatore web molto più veloce e gradevole, ma anche e soprattutto più efficiente e meno soggetto ad errori e distrazioni.