Construcción efectiva con Gulp: Parte 2

Continuamos con el segundo artículo de esta serie dedicada a Gulp. No olvides revisar el capítulo anterior de introducción o el próximo acerca de otros aspectos adicionales.

Extensiones interesantes

Si quieres saber qué extensiones puedes utilizar en Gulp, la página de referencia es http://gulpjs.com/plugins/.

En esta página, los plugins que veamos etiquetados como gulpfriendly, no son plugins como tal, pero están diseñados para funcionar de forma correcta en el entorno de Gulp. En este sentido, es conveniente tener cuidado cuando buscamos módulos a utilizar en la página de Npm, ya que no vemos algunos de los tags que aquí se detallan como por ejemplo blacklisted.

Recuerda que para usar un plugin es siempre necesario instalarlo vía Npm con la opción --save-dev.

Minificación de scripts

La minificación de scripts permite reducir el tamaño de fichero de nuestros fuentes, con lo que su descarga es más rápido. Tenemos muchas opciones disponibles, pero una de la más utilizada es uglify.

Para instalar uglify ejecutaremos el siguiente comando:

1
npm install gulp-uglify --save-dev

Para este ejemplo, crearemos un directorio js y copiaremos nuestros ficheros JavaScript dentro.

La definición de nuestro gulpfile.js tendría que ser algo así:

1
2
3
4
5
6
7
8
var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task('scripts', function () {
gulp.src('js/*.js')
.pipe(ugflify())
.pipe(gulp.dest('dist'));
});

Si en el directorio js tenemos un fichero main.js con el siguiente contenido:

1
2
3
4
5
6
7
var variable = "prueba";
function funcion1() {
}
function funcion2() {
}

Tras la ejecución, tendremos en el directorio dist el fichero main.js resultante con el resultado:

1
function funcion1(){}function funcion2(){}var variable="prueba";

Concatenación de ficheros del mismo tipo

Minimizar el número de peticiones HTTP que tiene que realizar el navegador, es una de las mejores optimizaciones que podemos realizar en nuestros desarrollos web. Para ello, una forma es concatenar todos los ficheros de un mismo tipo (por ejemplo, JavasScript) en un único fichero final para luego minificarlo.

Para poder realizar la concatenación de ficheros, necesitamos tener instalado el siguiente módulo:

1
npm install gulp-concat --save-dev

Su uso es muy sencillo, simplemente tenemos que especificar el nombre del fichero destino antes de realizar la minificación:

1
2
3
4
5
6
7
8
9
10
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
gulp.task('scripts', function () {
gulp.src('src/*.js')
.pipe(concat('all.min.js'))
.pipe(uglify())
.pipe(gulp.dest('dist'));
});

Watch

Gulp ya cuenta internamente con una tarea que se llama watchy que permite detectar cualquier tipo de cambio en los ficheros especificados. Esta funcionalidad es muy útil cuando, después de modificar por ejemplo un fichero SASS, es necesario su procesado con el fin de generar el CSS final que tenemos que visualizar en la página. Esta tarea de generación es muy pesada si la realizamos manualmente, pero con watches algo que podemos automatizar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var gulp = require('gulp');
var sass = require('gulp-sass');
gulp.task('sass', function () {
gulp.src('sass/*.scss')
.pipe(sass())
.pipe(gulp.dest('dist'));
});
gulp.task('default', function() {
gulp.watch('sass/*.scss', ['sass']);
});
```
Por defecto, la función `watch` nos devuelve un `watcher`, el cual nos permite además ejecutar cualquier acción en el momento de detectar un cambio en los ficheros observados:
```javascript
gulp.task('default', function() {
var watcher = gulp.watch('sass/*.scss', ['sass']);
watcher.on('change', function (event) {
console.log('Tipo de evento: ' + event.type);
console.log('Path: ' + event.path);
});
});

Los tipos de eventos detectados son modificación, borrado y añadido de nuevos ficheros.

Por otra parte, podemos hacer watch en otros eventos además de change:

  • end: El watcherha finalizado.
  • error: Ha ocurrido un error.
  • ready: Elwatcher a detectado los ficheros especificados y está listo para escuchar cambios.
  • nomatch: La definición del GLOB no se puede resolver a ningún fichero.

Por último, el watcher cuenta con una serie de métodos que podemos invocar manualmente:

  • watcher.end(): Paramos el watcher.
  • watcher.files(): Lista de ficheros observados.
  • watcher.add(glob): Añadir ficheros al GLOB inicial
  • watcher.remove(filepath): Borrar un fichero del GLOB inicial.

Recarga automática del navegador

LiverReload

Este módulo, combinado con watch y una extensión para el navegador, permite recargar la página HTML visualizada cada vez que se detecta un cambio en un fichero de código fuente relacionado (CSS, JavaScript, CoffeeScript, etc).

Como siempre, lo primero es instalar el módulo:

1
npm install gulp-livereload --save-dev

A continuación, instalamos la extensión de livereloaden el navegador. Para el caso de Chrome, necesitaremos instalarla desde el Chrome Web Store accediendo a la siguiente dirección:
https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=es

Por último, hay muchas ocasiones en las que livereload da problemas si la página a la que estamos accediendo en el navegador, se visualiza localmente accediendo mediante file:// en lugar de http://.

Para evitar este problema podemos usar SimpleHTTPServer, un servidor web muy rápido y ligero que permite servir por HTTP cualquier directorio de nuestro sistema de ficheros ejecutando simplemente este comando en el directorio a exponer:

1
python -m SimpleHTTPServer

Una vez tenemos todos los componentes listos e instalados, crearemos un proyecto con un documento HTML que tenga una referencia al fichero CSS que estamos generando tras compilar nuestros estilos definidos en SASS.

Un posible ejemplo de este fichero es:

1
2
3
4
5
6
7
8
9
10
$ cat test.html
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/screen.css">
</head>
<body>
<h1>LiverReload Test</h1>
</body>
</html>

Este HTML hace referencia a screen.css. Este fichero CSS se compila a partir de screen.scss, el cual está almacenado en el directorio sass.

El fichero de Gulp para el escenario descrito sería pués:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var gulp = require('gulp');
var sass = require('gulp-sass');
var livereload = require('gulp-livereload');
gulp.task('sass', function () {
gulp.src('sass/*.scss')
.pipe(sass())
.pipe(gulp.dest('css'))
.pipe(livereload());
});
gulp.task('default', function() {
gulp.watch('sass/*.scss', ['sass']);
});

De esta forma y con SimpleHTTPServer arrancado, ya podemos acceder a la dirección local:

http://localhost:8000/test.html

Cuando accedemos por primera vez, tenemos que hacer clic sobre el icono de livereload con el fin de activarlo y comenzar a modificar nuestros ficheros .scss, viendo como el navegador se recarga automáticamente.

{<1>}LiveReload

Por supuesto, livereload funciona para cualquier tipo de fichero que estemos monitorizando con watch.

BrowserSync

BrowserSync representa una alternativa muy completa el famoso LiveReload. Aunque tienen en común la capacidad de recargar en el navegador los cambios que se producen, BrowserSync tiene un montón de funcionalidades más.

Cuando realizamos un cambio en una páina, BrowserSync la recarga, pero si es el CSS el que se ha visto modficado, se inyectan los cambios sin necesidad de realizar una recarga completa. Esto permite que el desarrollo de páginas ricas sea mucho más sencillo, ya que no es necesario volver a hacer múltiples clicks para conseguir volver al estado en que estábamos en la página.

Otra de las funcionalidades más potentes de BrowserSync es la capacidad de sincronizar clicks, acciones en formularios o la posición del scroll entre los distintos navegadores. Es posible abrir varios naegadores de escritorio o móviles en emuladores y navegar la misma web de forma equivalente en todos al mismo tiempo.

Por último, BrowserSync no necesita de la instalación de un plugin en el navegador, ya que es capaz de enviar los cambios producidos directamente al navegador.

Aunque no existe un módulo de Gulp para BrowserSync debido al hecho de que no se realiza una manipulación directa de ficheros en el pipeline de construcción, sí que es posible utilizar directamente el módulo disponible a través de Npm.

Para ello sólo debemos instalar el módulo:

1
npm install browser-sync --save-dev

Y configurar los ficheros que vamos a monitorizar y recargar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var gulp = require('gulp'),
browserSync = require('browser-sync');
gulp.task('default', function () {
var files = [
'**/*.html',
'**/*.css'
];
browserSync.init(files, {
server: {
baseDir: '.'
}
});
});

El resultado es que, al ejecutar el comando gulp, BrowserSync comienza la monitorización y nos ofrece un enlace al que se deben conectar los navegadores que queramos sincronizar (en este caso http://192.168.1.103:3000):

1
2
3
4
5
6
[18:22:51] Using gulpfile /opt/devel/workspaces/programmer-at-work-examples/gulp/browsersync/gulpfile.js
[18:22:51] Starting 'default'...
[18:22:51] Finished 'default' after 5.15 ms
[BS] Local: >>> http://localhost:3000
[BS] External: >>> http://192.168.1.103:3000
[BS] Serving files from: .

Simplemente establenciendo esta dirección en Chrome, Firefox o IE (desde un VirtualBox si no estamos en Windows), podemos realizar pruebas sobre todos ellos al mismo tiempo sin ninguna configuración adicional.

Finalmente, tamibén podemos hacer pruebas con navegadores de dispositivos móviles al mismo tiempo. Por ejemplo, si estamos en MacOSX y tenemos instalado XCode, podemos utilizar el simulador de iPhone para testear Safari.

Procesamiento de ficheros CoffeeScript

Para poder generar JavaScript a partir de los ficheros CoffeeScript, necesitamos el módulo `gulp-coffee’:

1
npm install gulp-coffee --save-dev

Y posteriormente, su inclusión en el pipeline de construcción:

1
2
3
4
5
6
7
8
9
10
11
12
var gulp = require('gulp');
var concat = require('gulp-coffee');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
gulp.task('default', function () {
return gulp.src('coffee/*.coffee');
.pipe(coffee())
.pipe(concat('all.min.js'))
.pipe(uglify())
.pipe(gulp.dest('dist'));
});

Referencias

Puedes encontrar todos los ejemplos utilizados en este artículo en el repositorio de ejemplos de Programmer At Work