Front-end development toolkit deel 3Gulp plugins

Gulp op zich is al geweldig, maar met een breed scala aan plugins kun je jouw assets pipeline en front-end workflow pas echt goed optimaliseren. In het laatste deel van deze blogserie bespreek ik de belangrijkste plugins en hoe je ze samen kunt inzetten.

Een vliegende start met variabelen

Om bij iedere plugin niet steeds dezelfde variabelen los mee te hoeven geven, is het verstandig om in je .gulpfile na het inladen van de plugins een aantal variabelen te definiëren waar je bijvoorbeeld veel voorkomende paden in uiteenzet. Denk aan:

var imgSrc     = 'src/images/**';
var imgDest    = 'site/images';

var fontSrc    = 'src/fonts/**/*';
var fontDest   = 'site/fonts';

var scriptSrc  = 'src/script/**/*.js';
var scriptDest = 'site/script';

var stylesSrc  = 'src/styles/styles.scss';
var stylesDest = 'site/css';

var bowerDist  = 'site/dist';

Gulp-load-plugins

npm install gulp-load-plugins --save-dev

Gulp-load-plugins gebruik je om je Gulp-plugins te lazyloaden. Ze worden pas ingeladen op het moment dat je ze ook daadwerkelijk gebruikt. Tevens hoef je niet iedere plugin meer los handmatig in te laden in je .gulpfile.
Stel dat je een package.json hebt met de volgende dev dependencies:

  "devDependencies": {
    "gulp": "^3.9.1",
    "gulp-load-plugins": "^1.2.4",
    "gulp-rename": "^1.2.2",
    "gulp-sass": "^2.3.2"
  }

Dan zou je voorheen de plugins op de volgende manier inladen:

var gulp = require('gulp');
var plugins = require('gulp-load-plugins')();
var rename = require('gulp-rename');
var sass = require('gulp-sass');

Zoals je waarschijnlijk al verwacht kan dit, bij gebruik van veel plugins, een behoorlijk lange lijst worden. Moeilijk te onderhouden en vooral vervelend om te maken. Met gulp-load-plugins gaat dit gelukkig een stuk simpeler!:

var gulp = require('gulp');
var plugins = require('gulp-load-plugins')();

Alle Gulp-plugins worden nu (lazy) ingeladen op het moment dat je ze in een taak aanroept via de variabele: plugins.

plugins.sass();
plugins.rename();

En daarmee blijft je .gulpfile een stuk schoner en beter onderhoudbaar!

Voor de resterende voorbeelden in dit blog blijven we de Gulp-plugins ook op deze manier inladen.

del

npm install del --save-dev

Voor het verwijderen van mappen en bestanden is er del. Dit is geen Gulp-plugin, maar een gewone Node module. Deze moet je dus nog wel handmatig inladen in je .gulpfile.

Een typische use case van del is als je distributiemappen vervuild zijn geraakt door overbodige plugins. Als je Gulp bijvoorbeeld ook gaat inzetten in je automatische deployment pipeline, dan is het ook raadzaam om bij iedere deploy een verse build van je assets te doen, met vooraf een opschoning van de betreffende mappen. Om bijvoorbeeld .gitignores niet te verwijderen maken we hiervoor een exclude functie: delExcludeGitignore().

// Helper
function delExcludeGitignore(directory) {
 return del([directory + '/**/*.*', '!' + directory + '/.gitignore']);
}

// Clean images
gulp.task('clean:images', function () {
 return delExcludeGitignore(imgDest);
});

// Clean script
gulp.task('clean:script', function () {
 return delExcludeGitignore(scriptDest);
});

// Clean styles
gulp.task('clean:styles', function () {
 return delExcludeGitignore(stylesDest);
});

// Combined clean task
gulp.task('clean', ['clean:images', 'clean:script', 'clean:styles']);

gulp-contact

npm install gulp-concat --save-dev

Om bestanden samen te voegen gebruik je gulp-concat. Je kan een glob meegeven, dus bijvoorbeeld een lijstje van een paar bestanden of een wildcard selector. De bestanden worden samengevoegd in de volgorde zoals ze in je glob staan.

gulp.task('scripts', function() {
  return gulp.src('src/script/*.js')
    .pipe(plugins.concat('scripts.js'))
    .pipe(gulp.dest('site/script/'));
});

gulp-plumber

npm install gulp-plumber --save-dev

Om te voorkomen dat errors je hele assets pipeline stilleggen is gulp-plumber een handige plugin. Als je bijvoorbeeld een sass taak hebt, kun je plumber inzetten om fouten die optreden bij het genereren van een stylesheet weg te schrijven naar de console. Dit terwijl je watcher gewoon kan blijven draaien.

// Helper to handle error
var onSassError = function(error) {
	plugins.util.log('Sass Error', plugins.util.colors.red('123'));
	console.log(error);
	this.emit('end');
}

// Sass Task
gulp.task('sass', function() {
	 return gulp.src(stylesSrc, { base: 'src/styles' })
		.pipe(plugins.plumber({
			errorHandler: onSassError
		}))
		.pipe(plugins.sass()
		.pipe(gulp.dest(stylesDest))
	;
});

gulp-sass

npm install gulp-sass --save-dev

Syntactically Awesome Style Sheets, wie gebruikt ze tegenwoordig nog niet? Voorheen via Ruby, maar tegenwoordig via het razendsnelle Libsass. Voor gebruik met Gulp is er gulp-sass. Door includePaths mee te geven maak je verschillende bestanden, bijvoorbeeld in je bower_components beschikbaar voor imports. Let op als je gulp-sass gebruikt en je voorheen gewend was om Compass te gebruiken. Dat werkt niet meer. Gelukkig zijn er, met alle tools die nu tot je beschikking staan, voldoende mogelijkheden om de dingen die je met compass kon met Gulp te doen.

// Sass Task
gulp.task('sass', function() {
	 return gulp.src(stylesSrc, { base: 'src/styles' })
		.pipe(plugins.sass({
			includePaths: [
			    'bower_components',
			],
			outputStyle: 'compressed'
		}))
		.pipe(gulp.dest(stylesDest))
	;
});

gulp-autoprefixer

npm install gulp-autoprefixer --save-dev

Om minder tijd te besteden aan het ondersteunen van oude browsers zet je gulp-autoprefixer in. Door hem op te nemen in je sass taak voorzie je je stylesheets in een handomdraai van de juiste vendor prefixes.

// Sass Task
gulp.task('sass', function() {
	 return gulp.src(stylesSrc, { base: 'src/styles' })
		.pipe(plugins.sass({
			includePaths: [
			    'bower_components',
			],
			outputStyle: 'compressed'
		}))
		.pipe(plugins.autoprefixer({ browsers: ['last 10 versions'] }))
		.pipe(gulp.dest(stylesDest))
	;
});

gulp-sourcemaps

npm install gulp-sourcemaps --save-dev

Om na het samenvoegen van bijvoorbeeld javascript bestanden en het compileren van Sass bestanden nog te kunnen zien waar je de originele code hebt geschreven gebruik je zogeheten sourcemaps. Daarin wordt van elk stukje code het bronbestand en de betreffende regel bijgehouden. Voor gebruik met gulp is er gulp-sourcemaps. Onderstaand een voorbeeld hoe je gulp-sourcemaps kan inzetten in de eerder genoemde sass taak.

// Sass Task
gulp.task('sass', function() {
	 return gulp.src(stylesSrc, { base: 'src/styles' })
		.pipe(plugins.sourcemaps.init())
		.pipe(plugins.sass({
			includePaths: [
			    'bower_components',
			],
			outputStyle: 'compressed'
		}))
		.pipe(plugins.sourcemaps.write())
		.pipe(gulp.dest(stylesDest))
	;
});

Bij het inspecteren van onze code via de Chrome devtools zien we vervolgens de bronbestanden en op welke regel een declaratie is gedaan:

gulp-uglify

npm install gulp-uglify --save-dev

Nog een plugin waarbij je sourcemaps goed kan gebruiken is gulp-uglify. Doet precies wat de naam zegt, je code wordt er erg lelijk van, maar wel lekker compact (minification) en dus een stuk kleiner én sneller in te laden.

// Script task
gulp.task('scripts', function() {
  return gulp.src('src/script/*.js')
	.pipe(plugins.sourcemaps.init())
	.pipe(plugins.uglify())
	.pipe(plugins.sourcemaps.write())
    .pipe(gulp.dest('site/script/'));
});

gulp-imagemin

npm install gulp-imagemin --save-dev

Met gulp-imagemin kun je afbeeldingen kleiner maken zodat ze sneller inladen. Imagemin biedt standaard ondersteuning voor .gif, .jpg, .png en .svg afbeeldingen.

// Minify image task
gulp.task('imagemin', function() {
	return gulp.src(imgSrc )
		.pipe(plugins.imagemin())
        .pipe(gulp.dest(imgDest))
});

Na het draaien van de taak zie je in de console hoeveel besparing het minify-en van de afbeeldingen heeft opgeleverd:

[14:56:57] Using gulpfile D:\php\toolsblog\gulpfile.js
[14:56:57] Starting 'imagemin'...
[14:57:01] gulp-imagemin: Minified 1 image (saved 16.09 kB - 2.8%)
[14:57:01] Finished 'imagemin' after 4.47 s

browser-sync

npm install browser-sync --save-dev

Een échte time-saver is browser-sync. Geen gulp plugin, maar een gewone Node module. We moeten hem dus in de gulpfile als variabele definiëren:

var browserSync = require('browser-sync');

Met browser-sync kun je wijzigingen die je in je bronbestanden doet, direct laten verschijnen in een webbrowser. Wijzigingen in styles en html zelfs streamen, javascript wijzigingen met een reload. Onderstaand een voorbeeld van de combinatie van een eenvoudige sass taak en browser-sync, waarbij een watch taak de wijzigingen in de gaten houdt.

// Sass Task
gulp.task('sass', function() {
	return gulp.src(stylesSrc, { base: 'src/styles' })
		.pipe(plugins.sass())
		.pipe(gulp.dest(stylesDest))
		.pipe(browserSync.stream({match: '**/*.css'}))
	;
});

// Browser sync
gulp.task('browser-sync', function() {
	browserSync.init({
		server: "./site"
	});
});

// Watch for changes
gulp.task('watch', ['browser-sync'], function () {
  gulp.watch('src/styles/**/*.scss', ['sass']);
});

main-bower-files

npm install main-bower-files --save-dev

Met main-bower-files haal je eenvoudig alle benodigde bestanden van Bower dependencies naar je distributiemap. De module kijkt naar de main waardes van je dependencies en de overrides die je in bower.json hebt gedaan. Met de resultatenset die daar uit terug komt, kun je vervolgens allerlei handige dingen doen. In het volgende voorbeeld worden de bestanden naar de site/dist map gekopieerd:

// Copy bower assets to the dist folder
gulp.task('bower', function() {
	return gulp.src(mainBowerFiles(/* options */), { base: './bower_components' })
	.pipe(gulp.dest(bowerDist));
});

Alles tegelijk

Onderstaand een .gulpfile met daarin alle onderdelen die in dit blog zijn behandeld. Download ook het zip bestand met daarin alles wat we in deze blogserie hebben behandeld. Draai achtereenvolgens eerst npm install, bower install en gulp build. Vervolgens kan je met gulp watch de browser-sync server starten en het project in je webbrowser bekijken.

Ga er vooral mee aan de slag in je eigen projecten, durf dingen aan te passen en te proberen.

Veel succes!

gulpfile.js

// Load gulp and plugins
var gulp = require('gulp');
var plugins = require('gulp-load-plugins')();
var del = require('del');
var browserSync = require('browser-sync');
var mainBowerFiles = require('main-bower-files');

// Set variables
var imgSrc = 'src/images/**';
var imgDest = 'site/images';

var fontSrc = 'src/fonts/**/*';
var fontDest = 'site/fonts';

var scriptSrc = 'src/script/**/*.js';
var scriptDest = 'site/script';

var stylesSrc = 'src/styles/styles.scss';
var stylesDest = 'site/css';

var bowerDist = 'site/dist';

// Helper
function delExcludeGitignore(directory) {
	return del([directory + '/**/*.*', '!' + directory + '/.gitignore']);
}

// Clean images
gulp.task('clean:images', function () {
	return delExcludeGitignore(imgDest);
});

// Clean script
gulp.task('clean:script', function () {
	return delExcludeGitignore(scriptDest);
});

// Clean styles
gulp.task('clean:styles', function () {
	return delExcludeGitignore(stylesDest);
});

gulp.task('clean:bower', function () {
	return delExcludeGitignore(bowerDist);
});

// Combined clean task
gulp.task('clean', ['clean:images', 'clean:script', 'clean:styles', 'clean:bower']);

// Helper to handle error
var onSassError = function (error) {
	plugins.util.log('Sass Error', plugins.util.colors.red('123'));
	console.log(error);
	this.emit('end');
}

// Sass Task
gulp.task('sass', function () {
	return gulp.src(stylesSrc, {base: 'src/styles'})
		.pipe(plugins.sourcemaps.init())
		.pipe(plugins.plumber({
			errorHandler: onSassError
		}))
		.pipe(plugins.sass({
			includePaths: [
				'bower_components',
			],
			outputStyle: 'compressed'
		}))
		.pipe(plugins.autoprefixer({browsers: ['last 10 versions']}))
		.pipe(plugins.sourcemaps.write())
		.pipe(gulp.dest(stylesDest))
		.pipe(browserSync.stream({match: '**/*.css'}))
		;
});

// Script task
gulp.task('scripts', function () {
	return gulp.src('src/script/*.js')
		.pipe(plugins.sourcemaps.init())
		.pipe(plugins.concat('scripts.js'))
		.pipe(plugins.uglify())
		.pipe(plugins.sourcemaps.write())
		.pipe(gulp.dest('site/script/'));
});

// Minify image task
gulp.task('imagemin', function () {
	return gulp.src(imgSrc)
		.pipe(plugins.imagemin())
		.pipe(gulp.dest(imgDest))
});

// Browser sync
gulp.task('browser-sync', function () {
	browserSync.init({
		server: "./site"
	});
});


// Copy bower assets to the dist folder
gulp.task('bower', function () {
	return gulp.src(mainBowerFiles(/* options */), {base: './bower_components'})
		.pipe(gulp.dest(bowerDist));
});

// Build
gulp.task('build', ['clean', 'bower', 'scripts', 'sass', 'imagemin']);

// Watch for changes
gulp.task('watch', ['browser-sync'], function () {
	gulp.watch('src/styles/**/*.scss', ['sass']);
});

Geschreven door Maarten van Spil

Front-End Developer & UX Designer

Blijf up-to-date en ontvang updates in je mailbox

Lees ook deze interessante blogs

Is mijn website mobielvriendelijk?

Eerder schreven we al over het verchil tussen een mobiele website, een responsive website en een mobiele app. In dit artikel zoomen we wat verder in wat mobielvriendelijk eigenlijk is, waarom het belangrijk is en waar je op moet letten. Oké. Laten we van start gaan!

Mobiele site vs responsive website vs app

Eerder schreven we al over het belang van mobiel voor Google. Nu wil ik graag inzoomen op de verschillende opties om mobiele bezoekers te bedienen met een mooie online ervaring. Zoals de titel van het artikel al aangeeft zijn er drie smaken: Mobiele website, Responsive Website en IOS/Android (native) App. Ik start met het bespreken van de verschillende opties en  ik zal afsluiten met een conclusie hoe wij kijken naar welke oplossing je wanneer nodig hebt. 

Front-end development toolkit deel 2: Task runners

Om bij het bouwen van je website taken te automatiseren, zet je een (Javascript) task runner in. Deze zijn beschikbaar in allerlei smaken, zoals Grunt, Gulp & Broccoli. Klinkt niet allemaal even fris, maar dat is het wel! In dit blog aandacht voor Gulp in het bijzonder, want die vinden wij het lekkerst.