Ottimizzare sviluppo frontend con gulp

Come migliorare lo sviluppo frontend con Gulp

Pubblicato su 7 Gennaio, 2020 da Manuel Ricci in Web Development

Ad un certo punto del proprio percorso di crescita come frontend developer cominci a farti domande che non riguardano più il giusto font da usare o la giusta palette colori, ma cominci a domandarti se ciò che stai sviluppando con tanto amore è performante. Nella stragrande maggioranza dei casi, no.

Le motivazioni possono essere diverse, come ad esempio delle immagini non ottimizzate, il server configurato male o non adatto alle proprie esigenze oppure codice scritto male o superfluo per la pagina in uso.

Ho parlato in un articolo precedente del Critical CSS, ma forse è il caso di fare un passo indietro e spiegare per bene come Gulp possa ottimizzare un pochino il codice che scrivi. Attenzione! Un codice rindondante e scritto da cani, rimane un codice rindonante e scritto da cani. Gulp permette di ottimizzare dove possibile, ma cerchiamo di capire meglio di cosa sto parlando

Cos’è Gulp?

Gulp è un toolkit JavaScript che ti aiuta ad implementare delle task, utili ad automatizzare e semplificare il tuo workflow. Se usato correttamente può risolvere parecchie rotture di scatole in maniera del tutto automatica.

In questo articolo vedremo come implementare delle task di base, ma in futuro potrai sempre decidere di estenderle in base alle tue esigenze.

Installare Gulp

Prima di iniziare a scrivere codice, dobbiamo installare la dipendenza principale di Gulp, Node.js.

Se non hai Node installato sul tuo computer, puoi scaricarlo dal sito ufficiale.

Una volta installato Node, puoi installare Gulp usando la seguente istruzione da riga di comando (Terminale su Mac/Linux o Powershell su Windows).

// per utenti Windows
npm install gulp -g

// per utenti Mac/Linux
sudo npm install gulp -g

Nota: Gli utenti Mac e Linux devono anteporre la parola chiave sudo per eseguire il comando come amministratori.

La sintassi usata specifica di installare Gulp nel proprio computer usando npm (Node Package Manager).

La flag -g specifica che npm installerà Gulp globalmente nel sistema, questo ti permetterà di eseguire i comandi gulp ovunque nel tuo computer.

Ora che Gulp è installato possiamo creare un progetto per poterne vedere le potenzialità.

Creare un progetto

Sul tuo desktop crea una cartella con nome a piacere, la quale conterrà tutto il progetto. Apri la cartella nel prompt dei comandi (trascinala nel terminale) ed esegui il seguente comando

// Inizializza il progetto
npm init

Il comando qui sopra creerà un file chiamato package.json, il quale conterrà tutte le informazioni sul progetto, come le dipendenze, script da eseguire, ecc.

npm init ti mostrerà quanto segue:

Output dopo l'esecuezione del comando npm init
Output dopo l’esecuzione del comando npm init

Dopo la creazione del package.json, installiamo gulp a livello di progetto usando il seguente comando

npm install gulp --save-dev

Questo installerà gulp come una dipendenza di sviluppo del progetto invece che globalmente.

La flag --save-dev installerà e inserirà il nome del pacchetto sotto le dev dependency nel file package.json.

Quando l’esecuzione del comando sarà portata a termine nella tua cartella potrai notare una cartella node_modules, essa contiene le varie dipendenze e tende a diventare molto pensante.

Piccolo consiglio: se devi trasferire il tuo progetto a qualcuno non copiare la cartella node_modules, colui a cui la passerai dovrà semplicemente eseguire il comando npm init affinché npm legga il package.json e scarichi in automatico tutte le dipendeze del progetto. Stesso discorso vale se trasferisci il tuo progetto su Github o simili, escludi la cartella node_modules attraverso il file .gitignore.

Perfetto! Ora sei pronto, ma prima di proseguire oltre dobbiamo parlare un attimo di Gesù Cristo nostro signore come strutturare il nostro progetto.

Come strutturare la cartella del progetto

Gulp è abbastanza flessibile da lavorare con qualsiasi struttura, ma questo non vuol dire mettere tutti i file nella root directory. Per questo progetto useremo la seguente struttura.

Struttura cartelle progetto con Gulp
Una struttura del progetto ideale da usare con Gulp

In questa struttura, useremo la cartella src per lo svilippo, mentre dist (diminutivo di “Distribuzione”) conterrà i file ottimizzati per il sito di produzione.

La struttura del progetto dobbiamo tenerla a mente quando lavoriamo con Gulp. Il passaggio successivo è la creazione del file gulpfile.js (già presente nel mio screenshot), il quale conterrà tutte le task di Gulp.

Creare la prima Gulp Task

Come prima cosa dobbiamo richiamare gulp nel nostro file gulpfile.js

const gulp = require('gulp');

Giusto per capire la struttura di una task di Gulp, creiamone una inutile e chiamala “hello from gulp”.

gulp.task('hello', function (done) {
  console.log('Hello from Gulp');
  done();
});

La riga di comando mostrerà l’output Hello from Gulp quando il comando gulp hello verrà eseguito.

Output della task gulp Hello
Output della task gulp Hello nel terminale

Ovviamente, le task Gulp sono molto più complesse di così, contengono metodi addizionali e plugin.

Una vera task Gulp assomiglia di più a questo:

gulp.task('nome-task', function() {
	return gulp
		.src('file-sorgente') // Recupera file sorgente
		.pipe(unPluginGulp()) // Processalo con un plugin Gulp
		.pipe(gulp.dest('destinazione')); // Salvalo nella cartella di destinazione
});

gulp.src comunica alla task di Gulp quali file usare, mentre gulp.dest comunica dove salvare l’output dopo che la task termina la sua esecuzione.

Preprocessare con Gulp

In Gulp possiamo compilare file Sass in CSS con l’ausilio del plugin gulp-sass. Prima di tutto, installiamo il plugin come dipendenza di sviluppo usando il seguente comando:

npm install gulp-sass --save-dev

Dobbiamo richiamare il plugin all’interno del nostro gulpfile.js

const sass = require('gulp-sass');

Ora che il plugin è disponibile, possiamo creare una task che compili il file scss.

gulp.task('sass', function() {
	return gulp
		.src('src/scss/main.scss')
		.pipe(sass())
		.pipe(gulp.dest('src/css'));
});

La task, come menzionato poc’anzi, recupererà il file main.scss dalla cartella scss in src, eseguirà il plugin gulp-sass e salverà il risultato della task nella cartella css in src.

Per verificare che la task funzioni correttamente creiamo il file main.scss e aggiungiamo un paio di righe.

$primary-color: #bada55;
$font: 'Arial';

body {
	background: $primary-color;
	font-family: $font;
	p {
		color: #ffffff;
	}
}

Se da riga di comando esegui gulp sass, vedrai che automaticamente verrà creato il file main.css in src/css. Inoltre, se apri il file appena creato, vedrai che non sarà più in SCSS, ma in CSS.

body {
  background: #bada55;
  font-family: "Arial"; }
  body p {
    color: #ffffff; }

Meraviglioso, ora puoi compilare i file SASS in file CSS.

Sicuramente in futuro vorrai compilare più file SCSS, una delle peculiarità del linguaggio è proprio la facilità con la quale puoi frammentarlo in piccolo pezzi, semplificandone la manutenzione.

Per poter processare più file useremo i Node Globs.

Usare i Node Globs per processare file multipli

I Globs sono usati per confrontare i patterns.

Ci sono 4 globs differenti:

  1. *.scss: l’asterisco è una wildcard che confronta ogni file che termina con l’estensione .scss nella directory.
  2. **/*.scss: in questa versione l’asterisco confronta ogni file che termina con .scss nella cartella principale e in ogni cartella figlia.
  3. !not.scss: il punto esclamativo indica a gulp di escludere i file scss.
  4. *.+(scss|sass): il più e le parentesi permettono di creare pattern multipli, ogni pattern è separato da una pipe (la barra verticale).

Ora che conosci i globs, puoi procedere alla sostituzione di src/scss/main.scss con qualcosa di più “generico”, magari un pattern che controlli la root directory e ogni cartella figlia. Indovinato? Sì, è proprio src/scss/**/*.scss.

gulp.task('sass', function() {
	return gulp
		.src('src/scss/**/*.scss')
		.pipe(sass())
		.pipe(gulp.dest('src/css'));
});

Da questo momento potrai scrivere file SCSS multipli e compilarli con il comando gulp sass da terminale.

Aspettare che un file SASS cambi

Giunti a questo punto forse ti starai probabilmente chiedendo, ma devo eseguire ogni volta il comando gulp sass? Se così fosse, sarebbe una palla eseguirlo ogni sacrosanta volta.

Fortunatamente Gulp viene in nostro soccorso con il metodo watch, il quale controlla se vengono effettuate delle modifiche ai file desiderati ed esegue una task.

Per controllare eventuali modifiche ai file SCSS scriveremo nel gulpfile.js

gulp.watch('src/scss/**/*.scss', gulp.series('sass'));

Magari piuttosto che lasciare un comando così “volante” nel file è meglio creare una task

gulp.task('watch', function() {
	gulp.watch('src/scss/**/*.scss', gulp.series('sass'));
});

Ora se esegui gulp watch nella riga di comando la task resterà in esecuzione, in attesa che un file scss venga salvato per eseguire automaticamente la task sass.

In pratica abbiamo automatizzato, l’automazione.

CSS Vendor Free

Ancora oggi vengono usati quei dannatissimi vendor prefix (-moz-, -ms-, -o-, -webkit-), senza quelli alcuni browser non riconoscono la proprietà CSS, ignorandola. Un vero rompimento di palle.

Fortunatamente Gulp viene ancora una volta in nostro soccorso con il plugin gulp-prefixer. Esso ci permetterà di aggiungere automaticamente i prefissi, evitando una montagna di bestemmie durante lo sviluppo.

Installa il plugin con il seguente comando:

npm install gulp-autoprefixer --save-dev

Quindi modifichiamo la task sass per aggiungere i prefissi automaticamente

const autoprefixer = require('gulp-autoprefixer');
gulp.task('sass', function() {
	return gulp
		.src('src/scss/*.scss')
		.pipe(sass())
		.pipe(autoprefixer())
		.pipe(gulp.dest('src/css'));
});

Ora, eseguendo la task gulp sass il file scss verrà compilato in css con i prefissi, ove necessario.

Live-reload usando la sincronizzazione del browser

Save, Refresh, Repeat. Se scrivi CSS da un po’, sicuramente conosci la rottura di salvare, aggiornare la pagina, vedere che tutto è un bordello e tornare a scrivere codice.

Indovina un po’? Gulp ha un plugin che automatizza anche questo, si chiama browser-sync e si installa così:

npm install browser-sync --save-dev

Ad installazione terminata, possiamo scrivere la task.

const browserSync = require('browser-sync').create();
gulp.task('serve', function() {
	browserSync.init({
		server: 'src',
		port: 4000
	});
});

Eseguendo gulp serve da riga di comando l’applicazione verrà eseguita alla porta 4000.

Modifichiamo la task sass per sincronizzare il browser ogni volta che viene generato il file main.css

gulp.task('sass', function() {
	return gulp
		.src('src/scss/*.scss')
		.pipe(sass())
		.pipe(autoprefixer())
		.pipe(
			browserSync.reload({
				stream: true
			})
		)
		.pipe(gulp.dest('src/css'));
});

Ora configuriamo una task reload da passare a watch

gulp.task('reload', function(done) {
	browserSync.reload();
	done();
});
gulp.task('watch', function() {
	gulp.watch('src/scss/**/*.scss', gulp.series('sass', 'reload'));
});

Infine creiamo una task live-server per eseguire serve e watch, le quali rispettivamente avviano il browser sync e il controllo dei file.

gulp.task('live-server', gulp.series('serve', 'watch'));

Ottimizzare i file JavaScript e CSS

Mai sentito parlare di file minimizzati o minificati? Questo genere di azione può essere eseguita grazie ai toolkit come Gulp.

Supponiamo di aver incluso due script nell’index.html

<body>
    <!-- build:js js/main.min.js -->
    <script src="js/main.js"></script>
    <script src="js/custom.js"></script>
    <!-- endbuild -->
</body>

A sto giro useremo un plugin gulp chiamato gulp-useref che concatena i file CSS e JS in uno solo (un file CSS e un file JS) leggendo i commenti che iniziano con <!– build e finiscono con <!– endbuild –>. La sintassi è:

<-- build:<type> <path> -->
.............................( Html markup code for scripts )
<!—endbuild -->
  • <type> può essere js o css. Obbligatorio affinché si possa creare il file corretto.
  • <path> si riferisce al percorso finale.

Installiamo il plugin con la solita sintassi

npm install gulp-useref --save-dev

e creiamo la task

const useref = require('gulp-useref');
gulp.task('useref', function(){
   return gulp.src('src/*.html')
       .pipe(useref())
       .pipe(gulp.dest('dist'))
});

Eseguendo la task useref, Gulp leggerà i due script e li concatena in uno unico nella cartella dist/js/main.min.js

Risultato della task useref
Risultato della task useref

Per minificare JavaScript avremo bisogno di gulp-uglify e gulp-if

Installiamo i due pluggin con un comando unico

npm install gulp-uglify gulp-if --save-dev

Modifichiamo la task useref che minifica i file JavaScript.

const uglify = require('gulp-uglify');
const gulpif = require('gulp-if');
gulp.task('asset-opt', function(){
   return gulp.src('src/*.html')
       .pipe(useref())
       .pipe(gulpif('*.js', uglify()))
       .pipe(gulp.dest('dist'))
});

Per minificare il CSS invece dobbiamo inanzitutto richiamare il file nella head del documento index.html presente nella directory src

<!-- build:css css/main.min.css -->
<link rel="stylesheet" href="css/main.css" />
<!-- endbuild -->

Installa gulp-csso con il solito comando

npm install gulp-csso --save-dev

Modifichiamo la task asset-opt come segue:

gulp.task('asset-opt', function() {
	return gulp
		.src('src/*.html')
		.pipe(useref())
		.pipe(gulpif('*.js', uglify()))
		.pipe(gulpif('*.css', csso()))
		.pipe(gulp.dest('dist'));
});

Ora se il file sottoposto a scansione è un CSS verrà eseguita la funzione csso che lo minimizzerà. Testa la task eseguendo gulp asset-opt da linea di comando e vedrai che nella cartella dist verrà creato una nuova directory css con dentro il file main.min.css.

Copiare i file nella cartella Dist

Supponiamo di creare una cartella font dentro a src, la quale conterrà i font usati nel progetto.

Creiamo una task gulp che copia i file da src a dist

gulp.task('fonts', function(){
   return gulp.src('src/fonts/**/*')
         .pipe(gulp.dest('dist'))
});

Combinare più Gulp task

In precedenza abbiamo usato un metodo di Gulp chiamato series, di cui non ho spiegato nulla. Quel metodo permette di combinare più task simultaneamente.

Solitamente è utile creare una task per la produzione, la quale ottimizza tutti i file e li copia da src a dist.

Solitamente la chiamo build e non farò eccezione stavolta. La task eseguirà l’ottimizzazione dei CSS e dei JS e sposterà i font.

gulp.task('build', gulp.series('asset-opt', 'fonts'));

L’altra task è invece, quella per lo svilippo, l’abbiamo già creata. Si occuperà di compilare i file scss in css, controllare se ci sono cambiamenti e refresherà il browser di volta in volta usando browser-sync.

La task è quella che abbiamo chiamato live-server, ma la puoi rinominare default

gulp.task('default', gulp.series('serve','watch'));

Perfetto, abbiamo finito! Di seguito ti riporto i vari file creati manualmente

const gulp = require('gulp');
const sass = require('gulp-sass');
const autoprefixer = require('gulp-autoprefixer');
const browserSync = require('browser-sync').create();
const useref = require('gulp-useref');
const uglify = require('gulp-uglify');
const gulpif = require('gulp-if');
const csso = require('gulp-csso');

// gulp.task('hello', function(done) {
// 	console.log('Hello from Gulp');
// 	done();
// });

gulp.task('sass', function() {
	return gulp
		.src('src/scss/*.scss')
		.pipe(sass())
		.pipe(autoprefixer())
		.pipe(
			browserSync.reload({
				stream: true
			})
		)
		.pipe(gulp.dest('src/css'));
});

gulp.task('asset-opt', function() {
	return gulp
		.src('src/*.html')
		.pipe(useref())
		.pipe(gulpif('*.js', uglify()))
		.pipe(gulpif('*.css', csso()))
		.pipe(gulp.dest('dist'));
});

gulp.task('fonts', function() {
	return gulp.src('src/fonts/**/*').pipe(gulp.dest('dist/fonts'));
});

gulp.task('watch', function() {
	gulp.watch('src/scss/**/*.scss', gulp.series('sass', 'reload'));
});

gulp.task('serve', function() {
	browserSync.init({
		server: 'src',
		port: 4000
	});
});

gulp.task('reload', function(done) {
	browserSync.reload();
	done();
});

gulp.task('build', gulp.series('asset-opt', 'fonts'));

gulp.task('default', gulp.series('serve', 'watch'));
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<meta http-equiv="X-UA-Compatible" content="ie=edge" />
		<title>Document</title>
		<!-- build:css css/main.min.css -->
		<link rel="stylesheet" href="css/main.css" />
		<!-- endbuild -->
	</head>
	<body>
		<!-- build:js js/main.min.js -->
		<script src="js/main.js"></script>
		<script src="js/custom.js"></script>
		<!-- endbuild -->
	</body>
</html>
$primary-color: #bada55;
$font: 'Arial';

body {
	background: $primary-color;
	font-family: $font;
	p {
		color: #ffffff;
	}
}

Conclusioni

Oggi hai affrontato la base di Gulp, ma le potenzialità dietro al comando gulp sono infinite.

La maggior parte dei frontend developer troverà più che sufficienti quanto implementato durante questo articolo, ma Gulp può essere usato anche per ottimizzare lo sviluppo di plugin e temi WordPress.

Sia che ciò che abbiamo scritto è sufficiente, sia che le tue esigenze siano altre, ti lascio qui di seguito qualche plugin utile per estendere il tuo gulpfile.js

Fammi sapere come va e se hai bisogno non esitare a chiedere 😉

Immagine in evidenza di Clément H