logoCoderhouse.png
Ë
By Facundo Farias • December 1, 2015

Optimiza tu sitio sin esfuerzo: Guía práctica de Gulp

Soy Facundo Farias, desarrollador y profesor en Coderhouse, y les traigo un post que les hará ahorrar muchísimo tiempo a la hora de programar, que quizás hasta les deje tiempo para un chapuzón en la pileta este verano!

Como desarrolladores web, sabemos que hay tareas que repetimos constantemente para todos nuestros proyectos. Realizarlas manualmente consume tiempo y esfuerzo y con el tiempo se torna tedioso.

Por ejemplo, si quisiéramos adaptar esta plantilla de HTML5UP a nuestro gusto y optimizar los archivos antes de subirlos a nuestro servidor, nuestro proceso sería algo así:

  1. Compilar el código SASS y generar CSS cada vez que hacemos un cambio.
  2. Asegurarnos de optimizar cada imagen nueva, y recordarlo cada vez que las cambiemos.
  3. Quitar todo el CSS no usado y comprimirlo.
  4. Comprimir todo el código JavaScript.
  5. Comprimir todo el código HTML.
  6. Subir todo el código por FTP, SCP o GIT.

Hacer todo eso manualmente (¡y por cada mínimo cambio!) no solo es tedioso, sino que da lugar a errores. Además, hará que más de una vez tomemos atajos (como editar el código directamente en el servidor) por lo engorroso que resulta seguir el procedimiento.

Gracias a Gulp podemos hacer todo eso con un solo comando (y un poquito de JavaScript).

Gulp al rescate

Gulp es una herramienta que permite automatizar tareas. Funciona con NodeJS (si todavía no lo tenés instalado, este es un buen momento para hacerlo) y aprovecha el ecosistema de módulos para publicar “plugins”: tareas específicas que podemos reutilizar en nuestros proyectos.

Vamos a empezar creando un directorio de trabajo y ejecutando npm init para inicializar nuestro proyecto NodeJS. Dentro de ese directorio vamos a crear un archivo gulpfile.js y una carpeta src donde descomprimir la plantilla.

Hecho eso, instalamos Gulp como cualquier otro módulo de NodeJS. Como lo necesitamos para desarrollar, conviene guardarlo como dependencia de desarrollo:
npm install --save-dev gulp
Nuestro directorio debería quedar así:

$ ls -lX
total 16
drwxr-xr-x 12 ffarias ffarias 4096 nov 22 20:41 node_modules
drwxr-xr-x 4 ffarias ffarias 4096 nov 22 20:14 src
-rw-r--r-- 1 ffarias ffarias 1628 nov 22 21:39 gulpfile.js
-rw-r--r-- 1 ffarias ffarias 597 nov 22 20:42 package.json

Vamos a editar nuestro gulpfile.js con las tareas que queremos automatizar:

var gulp = require('gulp');

gulp.task('sass', function() {
console.log('1- Compilar el código SASS y generar CSS cada vez que hacemos un cambio');
});

gulp.task('img', function() {
console.log('2- Asegurarnos de optimizar cada imagen nueva, y recordarlo cada vez que las cambiemos');
});

// Dependencia: la tarea 'sass' se tiene que ejecutar antes de 'css'
gulp.task('css', ['sass'], function() {
console.log('3- Quitar todo el CSS no usado y comprimirlo');
});

gulp.task('js', function() {
console.log('4- Comprimir todo el código JavaScript');
});

gulp.task('html', function() {
console.log('5- Comprimir todo el código HTML');
});

// Dependencias: antes de ejecutar 'deploy' tenemos que ejecutar el resto de las tareas
gulp.task('deploy', ['html', 'css', 'js', 'img'], function() {
console.log('6- Subir todo el código por FTP, SCP o GIT');
});

gulp.task('default', ['deploy'], function() {
// Default task
});

Como vemos el código es bastante sencillo de leer. En principio requerimos gulp y luego definimos nuestras tareas con gulp.task. Algunas tareas pueden tener dependencias; por ejemplo, no queremos optimizar el CSS sin antes tener la última versión generada en base a nuestro código SASS.

Para ejecutar nuestras tareas, escribimos en la consola gulp NOMBRE_TAREA. Por ejemplo:

[16:13:34] Starting 'sass'...
1- Compilar el código SASS y generar CSS cada vez que hacemos un cambio
[16:13:34] Finished 'sass' after 140 μs

Si ejecutamos una tarea con dependencias, vamos a ver que el orden se respeta:

[16:15:01] Starting 'sass'...
1- Compilar el código SASS y generar CSS cada vez que hacemos un cambio
[16:15:01] Finished 'sass' after 105 μs
[16:15:01] Starting 'css'...
3- Quitar todo el CSS no usado y comprimirlo
[16:15:01] Finished 'css' after 46 μs

Si no indicamos el nombre de la tarea, vamos a ejecutar la tarea "default":

[16:15:55] Starting 'html'...
5- Comprimir todo el código HTML
[16:15:55] Finished 'html' after 119 μs
[16:15:55] Starting 'sass'...
1- Compilar el código SASS y generar CSS cada vez que hacemos un cambio
[16:15:55] Finished 'sass' after 46 μs
[16:15:55] Starting 'css'...
3- Quitar todo el CSS no usado y comprimirlo
[16:15:55] Finished 'css' after 37 μs
[16:15:55] Starting 'js'...
4- Comprimir todo el código JavaScript
[16:15:55] Finished 'js' after 29 μs
[16:15:55] Starting 'img'...
2- Asegurarnos de optimizar cada imagen nueva, y recordarlo cada vez que las cambiemos
[16:15:55] Finished 'img' after 28 μs
[16:15:55] Starting 'deploy'...
6- Subir todo el código por FTP, SCP o GIT
[16:15:55] Finished 'deploy' after 30 μs
[16:15:55] Starting 'default'...
[16:15:55] Finished 'default' after 2.93 μs

Acá vamos a ver algo interesante: las tareas no se ejecutan en el orden en que las escribimos, sino en el orden en que las llamamos, mediante dependencias. ¡Es importante definirlas!

Ahora que creamos nuestras tareas podemos empezar a trabajar en cada una de ellas, para que realmente hagan algo útil.

Tareas y plugins

Si estamos automatizando una tarea repetitiva, es probable que no seamos los primeros en intentarlo. Y si alguien ya lo hizo, posiblemente haya publicado un plugin.

Por ejemplo, googleando un poco vamos a encontrarnos con el plugin gulp-sass, que sirve justamente para compilar SASS y transformarlo en CSS. Vamos a usarlo para nuestra primera tarea. Para ello lo instalamos (y lo guardamos como dependencia en nuestro proyecto):
npm install --save-dev gulp-sass
Una vez instalado podemos requerirlo en nuestro gulpfile, y usarlo en nuestra tarea:

var gulp = require('gulp'),
sass = require('gulp-sass');

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

Para probarlo, ejecutamos en la línea de comandos
gulp sass
Y entre los resultados vamos a ver algo como esto:

[23:08:42] Starting 'sass'...
[23:08:42] Finished 'sass' after 125 ms

Si parece que no pasó nada, probá borrar de src/assets/css los archivos ie8.css, ie9.css y main.css, y volver a ejecutar la tarea. ¡Se vuelven a crear!

Probemos con la siguiente tarea: optimizar imágenes. Esta vez vamos a usar gulp-imagemin:
npm install --save-dev gulp-imagemin

var gulp = require('gulp'),
sass = require('gulp-sass'),
imagemin = require('gulp-imagemin');

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

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

Ejecutamos gulp img y vemos resultados como estos:

[23:17:08] Starting 'img'...
[23:17:08] gulp-imagemin: Minified 14 images (saved 12.7 kB - 12.9%)
[23:17:08] Finished 'img' after 187 ms

Acabamos de optimizar 14 imágenes con uno único comando, y redujimos casi un 13% el peso :) Esta vez, las imágenes optimizadas las copiamos a la carpeta dist.

Vamos con una tarea un poco más "complicada", optimizar CSS:

gulp.task('css', ['sass'], function() {
return gulp.src('src/assets/css/*.css')
.pipe(autoprefixer({
browsers: ['last 2 versions']
}))
.pipe(uncss({
html: ['src/*.html']
}))
.pipe(minifyCSS())
.pipe(gulp.dest('dist/assets/css'));
});

En este caso estamos usando varios plugins en la misma tarea (¡no olvides instalarlos!):

  • gulp-autoprefixer para asegurarnos de tener los prefijos correctos en nuestras declaraciones CSS (por lo menos para las últimas dos versiones de los principales navegadores)
  • gulp-uncss para quitar el CSS no usado (por cualquier archivo .html dentro de la carpeta src)
  • gulp-minify-css para comprimir todo.

Después de ejcutar gulp css, pasamos de 278.1KB a 48.4KB de CSS. Y como la tarea css depende de la tarea sass, nos aseguramos de trabajar siempre sobre la última versión del código.

Conclusión

Como vemos, usando gulp, los plugins escritos por la comunidad y un poco de JavaScript para juntar todo, podemos automatizar todas las tareas repetitivas de nuestros proyectos.

Para nuestro ejemplo, un posible archivo final quedaría así:

var gulp = require('gulp'),
sass = require('gulp-sass'),
sourcemaps = require('gulp-sourcemaps'),
imagemin = require('gulp-imagemin'),
autoprefixer = require('gulp-autoprefixer'),
uncss = require('gulp-uncss')
minifyCSS = require('gulp-minify-css'),
minifyHTML = require('gulp-minify-html'),
uglify = require('gulp-uglify'),
scp = require('gulp-scp2');

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

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

gulp.task('css', ['sass'], function() {
return gulp.src('src/assets/css/*.css')
.pipe(autoprefixer({
browsers: ['last 2 versions']
}))
.pipe(uncss({
html: ['src/*.html']
}))
.pipe(minifyCSS({compatibility: 'ie8'}))
.pipe(gulp.dest('dist/assets/css'));
});

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

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

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

gulp.task('deploy', ['html', 'css', 'js', 'img', 'fonts'], function() {
return gulp.src('dist/**/*')
.pipe(scp({
host: '111.111.111.111',
username: 'miusuario',
password: 'cL4v3',
dest: '/var/www/misitio.com/'
}))
.on('error', function(err) {
console.log(err);
});
});

gulp.task('default', ['deploy'], function() {
// Default task
});

¡Con 70 líneas tenemos automatizado prácticamente todo el workflow!

Por supuesto, qué plugins elegir va a depender del proyecto, pero hay de todas formas y colores. Y ya no hay excusas para entregar sitios sin optimizar :)

¡Ahora es tu turno! ¿Qué tareas repetís constantemente en tu día a día? Automatizalas con gulp