Task-runner

Mon workflow Front-end avec Gulp

Cet article vous présente le workflow Front-end Gulp que j’utilise pour mon portfolio.

workflowMon porfolio étant mon site perso, je l’utilise pour tester des nouveaux outils et nouvelles techno, mais également je suis amenée à le mettre à jour régulièrement.

Dans un but de gain de productivité, j’ai mis en place un « task runner » avec Gulp.

Consultez mon précédent article plus de détails sur les plugins Gulp que j’utilise.

Voici l’arborescence de mon projet (mon site utilise AngularJS, mais cette arbo peut être adaptée à n’importe quel site) :

– Tous mes fichiers source de développement sont dans le répertoire src/

– Tous mes fichiers compilés à mettre en prod sont dans le répertoire dist/

|-- dist/ (prod)
|  |-- app/
|  |  |-- common/
|  |  |  |--views.html
|  |  |-- global-5e89cfe23c.min.js
|  |-- css/
|  |  |-- fonts/
|  |  |-- styles-f6cbb1c1db.min.css
|  |-- images/
|  |-- index.php
|-- src/ (dev)
|  |-- app/
|  |  |--lib/
|  |  |  |--angular.min.js
|  |  |  |--jquery.min.js
|  |  |-- common/
|  |  |  |-- controller.js
|  |  |  |-- views.html
|  |  |-- app.js
|  |-- css/
|  |  |-- fonts/
|  |-- images/
|  |-- sass/
|  |  |--style_une.scss
|  |  |--style_deux.scss
|  |-- index.php
|
|-- node_modules/
|
|-- gulpfile.js

Je charge les modules dont je vais avoir besoin au début de mon fichier gulpfile.js et je définis mes variables.

var gulp = require('gulp');
var sass = require('gulp-sass'); 
var autoprefixer = require('gulp-autoprefixer');
var csso = require('gulp-csso');
var uglify = require('gulp-uglify');
var imagemin = require('gulp-imagemin');
var cache = require('gulp-cache');
var ngAnnotate = require('gulp-ng-annotate');
var critical = require ('critical').stream;
var rev = require('gulp-rev');
var revReplace = require('gulp-rev-replace');
var useref = require('gulp-useref');
var gulpIf = require('gulp-if');
var del = require('del'); 

var source = './src'; // dossier de travail de dev 
var prod = './dist'; // dossier de prod

Mes tâches de dev

Je définis mes tâches de Build qui me servent principalement en phase de dev : mes fichiers JS et CSS ne sont ni minifiés ni concaténés.

Mes autres fichiers sont juste déplacés et mes images optimisées.

Tout d’abord je réalise une tâche « clean » qui me permet de supprimer tous les fichiers de mes répertoires app et css.

Ma tâche css me permet de compiler mon code SASS en CSS de le préfixer. Ensuite je copie mon css dans mes répertoires de dev et de prod.

Pour ma tâche image, j’utilise un cache pour éviter de recompresser les images à chaque fois. Je prévois également une tâche clear si je veux vider ce cache.

// -------------------------------------------
// Tâches de Build : clean, css, movecss, files, js, img, fonts
// -------------------------------------------

//Tâche "clean"
gulp.task('clean', function(){
  return del([prod+'/app/', prod + '/css/']);
});

// Tâche "css" = SASS + autoprefixer (src -> prod)
gulp.task('css', function () {
  return gulp.src(source + '/sass/**/*.scss')
    .pipe(sass())
    .pipe(autoprefixer())
    .pipe(gulp.dest(source + '/css'))
    .pipe(gulp.dest(prod + '/css'));
});


//Tâche "js" = simple copie des fichiers (src -> prod)
gulp.task('js', function () {
  return gulp.src( [source +'/app/**/**/*.js'])
    .pipe(gulp.dest(prod + '/app'));
});


// Tâche "img" = Images optimisées (src -> prod) 
gulp.task('img', function() {
  return gulp.src(source+"/**/*.+(jpg|png|gif)")
   .pipe(cache(imagemin({ optimizationLevel: 7, progressive: true, interlaced: true })))
   .pipe(gulp.dest(prod))
});

//Tâche "clear" pour vider le cache
gulp.task('clear', function (done) {
  return cache.clearAll(done);
});

// Tâche "fonts" = simple copie des fontes (src -> prod)
gulp.task('fonts', function () {
  return gulp.src(source + '/css/fonts/*')
    .pipe(gulp.dest(prod + '/css/fonts'));
});

// Tâche "files" = simple copie des html/php/json/xml/etc. (src -> prod)
gulp.task('files', function () {
  return gulp.src([source + '/**/**/*.+(html|php)', source + '/*.+(php|html|json|xml|pdf)'])
    .pipe(gulp.dest(prod));
});

Mes tâches de prod

Je définis ensuite mes tâches de prod :

// ------------------------------------------------------------------
// Tâches de Prod 
// ------------------------------------------------------------------

//Tâche reduceFile (src --> prod)
gulp.task ('reduceFile', function() {

 return gulp.src(source + '/index.php')
 .pipe(useref()) // Concatène avec gulp-useref
 .pipe(gulpIf('*.js', ngAnnotate())) // traite les injections de dépendance AngularJS
 .pipe(gulpIf('*.js', uglify())) // Minifie le JS
 .pipe(gulpIf('*.css', csso())) // Minifie le CSS
 .pipe(gulpIf('*.js', rev())) //ne renomme que les fichiers js
 .pipe(gulpIf('*.css', rev())) //ne renomme que les fichiers css
 .pipe(revReplace({replaceInExtensions: ['.php']})) // Remplace les noms de fichiers dans les extentions php par ceux de rev
 .pipe(gulp.dest(prod));
});

// Tâche "critical" = critical inline CSS (prod -> prod)
gulp.task('critical', function () {
  return gulp.src(prod + '/index.php')
    .pipe(critical({
      base: prod,
      inline: true,
      width: 320,
      height: 480,
      minify: true
    }))
    .pipe(gulp.dest(prod));
});

La tâche reduceFile va lire avec gulp-useref tous les fichiers CSS et JS appelés dans le code HTML puis va les concaténer. A noter : elle va les concaténer dans l’ordre de déclaration du code HTML.

Les fichiers JS vont être traités avec ngAnnotate() pour gérer les injections de dépendance AngularJS puis minifiés avec uglify.

Mes CSS vont être minifiées avec gulp-csso.

Les fichiers ainsi minifiés et concaténés vont être renommés avec le composant rev pour gérer le versionning puis inclus dans mon code HTML.

Mise à jour 16/02/2016 : J’ai actualisé le code pour prendre en compte la version 3 de gulp-useref

Dans mon HTML :

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

.....
<!-- build:js app/global.min.js -->	
  <script src="app/lib/jquery-2.2.min.js" ></script>
...
  <script src="app/portfolio/portfolioServices.js" ></script>
<!-- endbuild -->

Ce qui va donner au final :

<link rel="stylesheet" href="css/styles-f6cbb1c1db.min.css">

.....
<script src="app/global-5e89cfe23c.min.js"></script>

Ma tâche critical, quant à elle, va me générer du CSS inline pour accélérer le processus de chargement de la page.

Lancer mes tâches

Je lance mes tâches de manière séquencielle, c’est-à-dire certaines à la suite d’autres. J’utilise pour cela le plugin gulp-sequence

Pour lancer ma tâche de dev :

var gulpSequence = require('gulp-sequence');

// Tâche "build" : sert pour le dev
gulp.task('build', gulpSequence('clean', ['css', 'js', 'img', 'fonts', 'files' ]));

Je lance en 1er ma tâche de clean pour partir d’un répertoire clean (comme son nom l’indique 😉 ), puis ensuite je lance mes autres tâches.

Pour la lancer ma tâche de prod :

var gulpSequence = require('gulp-sequence');

// Tâche "prod" tâche de mise en prod
 gulp.task('prod', gulpSequence('clean', 'css', [ 'img', 'fonts', 'files'], 'reduceFile', 'critical'));

Je lance en 1er ma tâche pour nettoyer mon répertoire, puis la tâche css (qui va compiler mon sass en css) . Cette tâche doit être lancée avant ma tâche reduceFile.

Lancer automatiquement mes tâches

Quand je suis en phase de dev, pour ne pas avoir à relancer mes tâches manuellement, je lance ma tâche watch.

Pour ne pas avoir à faire refresh sur mon navigateur, dès que j’ai fait une modif, j’utilise l’outil browserSync. Ce dernier permet d’actualiser et de synchroniser automatiquement ses pages web et de pouvoir tester son site simultanément sur plusieurs navigateurs.

Quand je lance ma tâche watch Gulp va lancer les tâches build et browser-Sync puis, à chaque changement de fichier lancer la tâche associée et rafraîchir mon navigateur :

Par exemple, dès que je modifie un fichier Sass, la compilation se fait automatiquement et mon navigateur est synchronisé avec la mise à jour.

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

gulp.task('browser-Sync', function() {
  browserSync.init({
    proxy: 'http://localhost/.../dist/'
  })
})
gulp.task('watch', ['browser-Sync', 'build'], function (){
 gulp.watch([source +'/app/**/**/*.js'], ['js', browserSync.reload] );
 gulp.watch([source + '/sass/**/*.scss' ], ['css', 'movecss', browserSync.reload] ); 
 gulp.watch([source + '/**/**/*.+(html|php)' ], ['files', browserSync.reload] ); 
});

Et voilà !

Maintenant que vous maîtrisez Gulp, mettez en place votre projet en 5 minutes en utilisant un workflow déjà tout fait avec Yeoman !

Une réponse à “Mon workflow Front-end avec Gulp

  1. Bonjour,
    merci pour votre article intéressant.
    J’utilise depuis peu gulp pour mes dev worpdress.
    J’aurais une question concernant critical.
    Critical permet de séparer le css pour accélerer l’affichage.
    Vue qu’un site comporte plusieurs pages qui peuvent avoir du css différents sur le haut du site, comment intégrer critical à l’ensemble du site et notamment wordpress qui fonctionne en template de pages ?

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *