Grunt: come minimizzare file css e javascript, ed ottimizzare le immagini
Grunt è un task runner, cioè un programma in grado di automatizzare uno o più processi che altrimenti dovrebbero essere eseguiti manualmente.
Ci siamo occupati di introdurre Grunt e di come installarlo, quindi ripartiamo dall'articolo precedente... e se non lo avete letto fatelo adesso perchè è la premessa a quanto andremo a spiegare.
Nella cartella del nostro progetto, chiamata "progettoGrunt", oltre ai file e directory creati in fase di creazione del progetto, abbiamo questa architettura:
- src: directory che contiene i file css, javascript ed immagini sorgenti, che verranno eleborate da Grunt
- dst: directory che contiene i file destinati alla produzione, cioè elaborati da Grunt
- test: contiene il codice per testare il progetto (cioè il o i file html che conterranno i file ottenuti dalla elaborazione di Grunt)
- Gruntile.js: file javascript dove inseriamo i task
Oggi vediamo come creare alcuni task per ottimizzare file css, javascript ed immagini presenti nel nostro progetto web.
I file sorgenti saranno quindi inserira nella directory "src" dove all'interno creiamo 3 cartelle, una destinata ai css, una ai javascript, una alle immagini.
src
- js
- css
- images
Quando si parla di minimizzare o minificare i file css e javascript intendiamo l'operazione che comprime al massimo i file.
Ad esempio prova ad aprire questo file "minificato" di JQuery: come puoi vedere il codice è compatto e ridotto al minimo. Questo significa un peso minore del file.
Il nostro obbiettivo e quello di ottenere lo stesso risultato partendo dai file css, javascript ed immagini del nostro sito.
Ottimizzare i file JAVASCRIPT
Vediamo iniziamlemente come ottimizzare i JAVASCRIPT. L'obiettivo è accorpare tutti i file js in un file unico a cui daremo il nome "all.js", che dovremo anche minificare.
Quindi presupposto è mettere nella cartella "src/js" tutti i javascript che dovranno subire questo "trattamento"
Per fare questo, come prima cosa, dobbiamo installare alcuni plugin, che installeremo a livello del progetto (non a livello globale).
- grunt-contrib-uglify: serve a minificare i file javascript
https://github.com/gruntjs/grunt-contrib-uglify - grunt-contrib-jshint: tool che individua errori, potenziali problemi e suggerimenti al codice JavaScript.
https://github.com/gruntjs/grunt-contrib-jshint - grunt-contrib-concat: serve a concatenare i nostri files js creandole uno unico
https://github.com/gruntjs/grunt-contrib-concat - grunt-contrib-watch: serve ad indentificare quali files sono stati modificati
https://github.com/gruntjs/grunt-contrib-watch
Accediamo alla cartella del progetto
# cd /var/www/progettoGrunt
ed installiamo questi plugin
# npm install grunt-contrib-uglify --save-dev
# npm install grunt-contrib-jshint --save-dev
# npm install grunt-contrib-concat --save-dev
# npm install grunt-contrib-watch --save-dev
Installati i plugin, apriamo il "Gruntfile.js" e scriviamo questo codice
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dst/<%= pkg.name %>.js'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dst/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'dst/**/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
};
Analizziamo il codice partendo dal fondo
Abbiamo inizializzato i vari plugin con "grunt.loadNpmTasks"
Abbiamo creato un task "default" che a sua volta esegue in un sol colpo i task "jshint", "concat", "uglify".
Nel "grunt.initConfig" indichiamo le proprietà dei singoli task. Ma prima di questi abbiamo l'istruzione "pkg: grunt.file.readJSON('package.json')," che legge il contenuto del file "package.json" da cui andremo a prelevare alcune informazioni, che scriveremo sul file finale, nel nostro esempio il "name" del progetto.
Analizziamo adesso i singoli task.
Il task jshint: con questo task verifichiamo la presenza di problemi nel codice javascript dei nostri files, l'uso dell'operatore di uguaglianza invece dell'operatore di uguaglianza rigorosa e la definizione di variabili e funzioni non utilizzate.
Diamo in pasto a jshint tutti i file .js presenti nel nostro progetto, presenti sia nella cartella "src" che "dst" che il "Gruntfile.js".
Il task concat: preleva tutti i file presenti nel suo target cioè la direcrory "src", ed eventuali sue sottodirectory, che hanno come estenzione .js, e crea un unico file sotto la cartella "dst" che avrà come nome il "name" del package, prelevato dal file "package.json" ed estensione .js
Questo target ha un nome: "dist" (ma potevamo dargli un nome qualunque). Questo nome verrà richiamato dal task uglify.
Il task uglify: andrà a creare, partendo dal file creato dal task precedente (<%= concat.dist.dest %>), un file minificato che avrà come nome il "name" del package ed estensione ".min.js"
Il task watch: quando viene rilevato che uno dei file javascript dati in pasto a jshint viene modificato, verrà eseguito il task jshint. Tra poco lo vediamo in azione.
Bene, prima di lanciare il task default, proviamo il task jshint
# grunt jshint
Se sono presenti errori nel file verranno evidenziati con in questo esempio e veniamo inviatati a correggerli
Running "jshint:files" (jshint) task
src/js/contatti.js
25 | var lnh = email.length;
^ 'lnh' is already defined.
>> 1 errors in 2 files
Warning: Task "jshint:files" failed. Use --force to continue.
Aborted due to warnings.
Provvediamo alla correzione e rilanciamo il task... adesso il file è corretto.
Running "jshint:files" (jshint) task
>> 2 files lint free.
Done.
Adesso vediamo cosa fa il task watch. che non inseriamo nel task default, cioè non servirà per minimizzare i file, ma ha la sua utilità che adesso vediamo.
# grunt watch
L'output sarà il seguente
Running "watch" task
Waiting...
Il task resta in ascolto....
Non chiudiamo il terminale, e proviamo a modificare uno dei file javascript presenti nella cartella "src" (nel nostro esempio un file chiamato contatti.js) .... non appena fatta la modifica il task watch si sveglierà dal suo stato di attesa e visualizzerà quanto segue
Running "watch" task
Waiting...
>> File "src/js/contatti.js" changed.
Running "jshint:files" (jshint) task
>> 2 files lint free.
Done.
Completed in 0.268s at Wed Sep 30 2020 13:39:59 GMT+0200 (Central European Summer Time) - Waiting...
In poche parole, ha verificato che un file è stato modificato, e l'ha passato al task jshint per la verifica.
Siamo quindi finalmente al lancio del task default, con cui possiamo procedere all'esecuzione di tutti i task
# grunt
Apriamo la cartella "dst" e troveremo il file "all.js" composto dall'unione di tutti i nostri file js, ottimizzati e minificati.
Ottimizzare i file CSS
In aggiunta ai task precedenti, vogliamo concatenare, cioè inserire in un unico file, anche i file css, come abbiamo fatto per i js.
Il task concat in questo momento ha un unico "target" chiamato "dist" cioè un unico obbiettivo: i files js contenuti nella cartella "src"
dist: {
src: ['src/**/*.js'],
dest: 'dst/<%= pkg.name %>.js'
}
Adesso modifichiamo il task aggiungendo un secondo target, che chiamiamo ad esempio "mycss", che preleva i file css dalla cartella "src" e da questi crea un unico file che avrà come nome il "name" del package
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dst/<%= pkg.name %>.js'
},
mycss: {
src: 'src/**/*.css',
dest: 'dst/<%= pkg.name %>.css'
}
},
Il primo step per ottimizzare i css è completato. Lanciando questo task verrà creato un unico file css.
Adesso dobbiamo minificare questo file css creato dal task concat e per questa funzione utilizziamo il seguente plugin
- grunt-contrib-cssmin: si occupa di ottimizzare e minificare i file css
https://github.com/gruntjs/grunt-contrib-cssmin
Entriamo nella cartella del progetto (dovreste già esserci)
# cd /var/www/progettoGrunt
ed installiamo il plugin
# npm install grunt-contrib-cssmin --save-dev
Modifichiamo il "Gruntfile.js" aggiungendo questi plugin
grunt.loadNpmTasks('grunt-contrib-cssmin');
Aggiungiamo un task che chiamiamo "cssmin", che prenderà il file ".css" creato dal task concat, e lo minifica creando un file ".min.css"
cssmin: {
css: {
src: 'dst/<%= pkg.name %>.css',
dest: 'dst/<%= pkg.name %>.min.css'
}
}
Il "Grunfile.js" adesso avrà questo codice
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dst/<%= pkg.name %>.js'
},
mycss: {
src: 'src/**/*.css',
dest: 'dst/<%= pkg.name %>.css'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dst/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'dst/**/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
},
cssmin: {
css: {
src: 'dst/<%= pkg.name %>.css',
dest: 'dst/<%= pkg.name %>.min.css'
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'cssmin']);
};
Bene, lanciamo il task default e vediamo il risultato
#grunt
Nella cartella "dst" troveremo il file minificato "progettogrunt.min.css"
Ottimizzare le immagini
L'ultimo esempio che vediamo oggi è l'ottimizzazione delle immagini.
Mettiamo le immagini nella cartella "dst/images".
Utilizziamo il plugin "grunt-contrib-imagemin", che andiamo ad installare così
# npm install grunt-contrib-imagemin --save-dev
Modifichiamo il "Gruntfile.js" aggiungendo questo plugin
grunt.loadNpmTasks('grunt-contrib-imagemin');
Aggiungiamo un nuovo task chiamato imagemin che deve prelevare tutti i file della cartella "src/images" (ed eventuali sottocartelle) con estensione "png,jpg,gif" e creare dei file ottimizzati da inserire nella cartella "dst/images"
imagemin: {
options: {
optimizationLevel: 3,
svgoPlugins: [{removeViewBox: false}],
use: [mozjpeg()] // Example plugin usage
},
dynamic: {
files: [{
expand: true,
cwd: 'src/images/',
src: ['**/*.{png,jpg,gif}'],
dest: 'dst/images'
}]
}
}
Il nuovo task preleva le immagini dalla nostra cartella, le ottimizza, e le pone in una analoga cartella "images" all'interno della cartella "dst" (se non presente la crea)
Questo task utilizza la libreria "imagemin-mozjpeg" per la compressione dei file jpg, per cui procediamo anche alla sua installazione a livello globale visto che potremmo riutilizzarlo
# npm install imagemin
E adesso dobbiamo includerlo nel "Gulfile.js", così:
var mozjpeg = require('imagemin-mozjpeg');
Infine aggiungiamo al task default il nuovo task imagemin
grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'cssmin', 'imagemin']);
Il "Gruntfile.js" adesso sarà così
module.exports = function(grunt) {
var mozjpeg = require('imagemin-mozjpeg');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dst/<%= pkg.name %>.js'
},
mycss: {
src: 'src/**/*.css',
dest: 'dst/<%= pkg.name %>.css'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dst/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'dst/**/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
},
cssmin: {
css: {
src: 'dst/<%= pkg.name %>.css',
dest: 'dst/<%= pkg.name %>.min.css'
}
},
imagemin: {
options: {
optimizationLevel: 3,
svgoPlugins: [{removeViewBox: false}],
use: [mozjpeg()] // Example plugin usage
},
dynamic: {
files: [{
expand: true,
cwd: 'src/images/',
src: ['**/*.{png,jpg,gif}'],
dest: 'dst/images'
}]
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-imagemin');
grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'cssmin', 'imagemin']);
};
Bene, adesso lanciamo il task default, oppure semplicemente il task imagemin, a vostra discrezione
# grunt
Dentro la cartella "dst", se non era presente, è stata creata la cartella "images" e dentro troviamo le immagini ottimizzate con un peso ridotto.
Grunt e Gulp
Un altro task runner, molto simile a Grunt, è Gulp a cui abbiamo dedicato già un articolo.
La principale differenza tra i due è che Gulp gestisce i processi tramite la RAM (utilizza principalmente il modulo stream di Node.js) e li scrive immediatamente sul file di destinazione, mentre Grunt utilizza principalmente il modulo fs (file system), creando file temporanei mentre esegue le operazioni. Per questo motivo Gulp svolge le operazioni più velocemente.
Potrebbe interessarti
- YUI Compressor: minimizzare i file CSS e Javascript
- Grunt: introduzione, installazione e primo utilizzo
- Gulp: come minimizzare file css e javascript, compilare file scss, ed ottimizzare le immagini.
- Gulp: introduzione, installazione e primo utilizzo
- Utilizziamo Squeeze, la più famosa libreria PHP per comprimere file JAVASCRIPT e rendere quindi più veloce il nostro sito web.
- Utilizziamo la libreria PHP Minifier per comprimere file CSS e JAVASCRIPT e rendere più veloce il nostro sito web.