Con este tutorial tu día a día en el desarrollo web frontend, de diseñador web o incluso de programador web backend, cambiará.
En solo unos minutos te vas a montar un buen entorno de desarrollo web con un solo objetivo: AUTOMATIZAR TAREAS.
¿Qué vamos a automatizar? Tareas que haces continuamente de forma manual por ejemplo:
- Hacer que se recarge la página de tu navegador automáticamente cada vez que hagas cambios en un fichero, sin tener que actualizar tú el navegador de forma manual.
- Sincronizar pantallas de tablets, móviles y todos los dispositivos que quieras, es decir si haces scroll en una se hará en todas las pantallas, si haces click más de lo mismo, etc. Ideal para el desarrollo web responsivo, de un vistazo verás como queda todo sin tener que probar los dispositivos uno a uno.
- Escribir en SASS nuestros estilos y que al guardar los archivos .scss se compile automáticamente a un fichero CSS.
- Borrar el CSS que ya no usas en tu proyecto. Quizás debido a las prisas o por despiste te dejas rastros de basura de código en tu hoja de estilos o puede que uses frameworks frontend como Bootstrap o Foundation y te sobran clases. Con esta tarea puedes borrarte de un plumazo el código que no usas y reducir muchísimo el tamaño de tu archivo CSS.
- Minificar nuestros archivos css y JavaScript js para que ocupen menos peso en su puesta a producción. Por ejemplo: styles.min.css, scripts.min.js. Ya sabéis que Google valora que el tiempo de carga de una web sea rápido para el posicionamiento.
- Comprimir las imágenes de tu proyecto para reducir el tiempo de carga, con los formatos svg, jpg, png y gif.
Ya ves que la automatización significa reducir la inversión de tiempo y aumentar nuestra productividad, centrándonos en nuestros proyectos y dejando estas acciones en las manos de un gestor de tareas.
Automatizar tareas con Grunt.js y JavaScript
Grunt.js es un Task Runner (automatizador de tareas o ejecutor de tareas) en JavaScript que nos permite configurar tareas automáticas y así ganar productividad y reducir el tiempo de desarrollo y despliegue a producción de nuestras aplicaciones web.
Grunt trae un montón de plugins para automatizar tareas específicas (gruntplugins) y cada día van aumentando de número gracias a la gran comunidad que tiene detrás.
El resumen: Grunt.js usa un fichero javascript que tienes que crearlo en la raíz de tu proyecto y nombrarlo como Gruntfile.js, en ese fichero escribimos las tareas que queremos automatizar y finalmente con un comando en consola ejecutamos Grunt que gestionará por nosotros esas tareas.
Grunt necesita un poco de Node.js para instalarse como módulo en nuestros proyectos y para instalar sus gruntplugins a través de NPM (Node Package Manager).
Primero instalamos globalmente Grunt cli (command line interface) y luego accedemos al proyecto que tenemos ya creado para instalarle grunt y sus plugins.
¿Nodejs, JavaScript, Gruntjs? ¡Soy Diseñador Web y me suena a chino!: No temas amigo, no es necesario para nada ser experto en Node o JavaScript para montarte un pedazo de entorno de desarrollo web. Te animo a seguir leyendo, ya verás que el resultado final merece la pena.
1) Instalación de Grunt CLI
Grunt.js y sus plugins se instalan y se gestionan por NPM (Node Package Manager). Por lo tanto debes tener instalado Node.js en tu equipo, esto ya lo contamos una entrada del blog, pero no me importa recordarlo en esta entrada también. Si acabas de aterrizar en este tutorial y andas un poco perdido sobre qué es Nodejs, puedes visitar esta entrada donde lo explico un poco;)
Instala primero Node.js si no lo tienes
Simplemente entra en la web de node y descarga Node. Ejecuta el instalador y dale a continuar sin más, ¡ya tendrás instalado Node y npm!, lo más fácil del mundo.

Instalación de Node
Hacemos una doble comprobación, ve a la consola y escribe: node -v y npm -v.

node -v y npm -v
Si te aparece el temido «command not found» en una de las comprobaciones, asegúrate que node ha sido agregado a tu PATH. Esto nos permite usar node y los comandos npm en la línea de comandos de la terminal. Simplemente busca donde se instaló npm, copia la ruta, abre la ventana de variables de entorno y añade al path la ruta del npm. Reinicia tu ordenador y listo.
Si tienes otros problemas para instalar Node, consulta los siguientes enlaces:
- Windows: Añadir Node a tu PATH de Windows.
- Mac: Añadir Node a tu PATH de Mac.
Instalación de grunt-cli
Antes asegúrate que tienes actualizado NPM ejecutando este comando en la terminal npm update -g npm.
Ahora debemos instalar Grunt CLI (command line interface) de forma global en nuestro equipo, si usas Mac u otro sistema Unix quizás sea necesario añadir al principio de este comando la instrucción sudo (en el caso de que no tengas permisos):
1 |
npm install -g grunt-cli |
Este comando dejará preparado el PATH de Grunt en las variables de tu sistema, de esta forma puedes usarlo y ejecutarlo en cualquier directorio de tu ordenador.
Pero ojo, con esto no hemos instalado Grunt, la función de Grunt CLI es que te permite tener multiples versiones de Grunt instaladas en tu máquina, ya que Grunt.js puedes instalarlo de forma individual para cada uno de tus proyectos o si lo prefieres de forma global en tu equipo.
Se recomienda como buena práctica instalar grunt de forma individual en cada proyecto / directorio localmente y no de forma global, para así no tener conflicto entre versiones llegado el caso. También depende de la forma en que estés acostumbrado a trabajar.
2) Preparar nuestro proyecto para usar Grunt.js
Debemos añadir a la raíz de nuestro proyecto dos ficheros: Package.json y el Gruntfile.js.
Añadimos el fichero Package.json
El Package.json es el archivo de configuración de tu proyecto (que usa npm para gestionar las dependencias), y contiene una lista con las herramientas que instalaremos (llamadas devDependencies) y otros datos de información opcionales como el nombre del proyecto, la versión, autor, etc.
Para crear un fichero Package.json básico abrimos la consola o terminal, nos situamos en la raíz del proyecto(en mi caso voy a usar el del tutorial de MaterializeCSS) y ejecutamos la siguiente instrucción:
1 |
npm init |
Ahora la consola te va a bombardear a preguntas para crear el Package.json, no hace falta que leas mucho, solo pulsa enter o intro hasta llegar al final:
Para finalizar tienes que escribir yes y ya se creará el package.json en la carpeta de tu proyecto, en mi caso quedó así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "name": "tutorial-materialize", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/funnyfrontend/tutoriales-css3.git" }, "author": "funnyfrontend", "license": "ISC", "bugs": { "url": "https://github.com/funnyfrontend/tutoriales-css3/issues" }, "homepage": "https://github.com/funnyfrontend/tutoriales-css3#readme" } |
Si quieres puedes usar este package.json que he simplificado con la información básica necesaria:
1 2 3 4 5 6 |
{ "name": "tutorial-materialize", "version": "1.0.0", "description": "", "author": "funnyfrontend" } |
Instalamos Grunt localmente
Vamos a instalar la última versión de Grunt.js (el módulo) en las carpetas de nuestro proyecto, para ello introduce el comando:
1 |
npm install grunt --save-dev |
Recuerda usar sudo para Mac y sistemas unix.
Este comando no solo ha instalado el modulo grunt de forma local, si no que también ha añadido la sección devDependencies a tu package.json. Como dijimos antes, aquí se irán añadiendo las dependencias o herramientas que usemos en el proyecto, en este caso si abres el package.json verás que nuestra primera dependencia es Grunt.js y su versión actual:
1 2 3 |
"devDependencies": { "grunt": "^0.4.5" } |
También se ha creado la carpeta node_modules, donde se irán guardando los plugins que vayamos instalando.
Añadimos el fichero Gruntfile.js
Las tareas que vamos a automatizar las indicamos en el fichero Gruntfile.js, que es un simple documento javascript. En él se cargan los plugins y la configuración de estas tareas. Les damos instrucciones de dónde encontrar nuestros archivos y qué hacer con ellos, también definimos el orden en que se van a ejecutar.
Al igual que el package.json, debemos crear el Gruntfile.js en la raíz de nuestro proyecto. Esto lo hacemos de forma manual. Abre un nuevo documento y nómbralo como Gruntfile.js y copia este contenido inicial dentro de él:
1 2 3 4 |
//Gruntfile.js module.exports = function(grunt) { // La configuración de las tareas se pone aquí }; |
Por ahora nos vale esto, más adelante añadiremos las tareas, que deben ir siempre dentro de la función de arriba.
Resumen
Estas son todas las configuraciones que necesitábamos, básicamente hemos hecho esto:
- Instalar Node.js
- Instalar grunt-cli globalmente, con el comando npm install -g grunt-cli
- Entrar en nuestro proyecto y crear el Package.json, con el comando npm init
- Instalar Grunt localmente en el proyecto con el comando, npm install grunt —save–dev
- Crear de forma manual el archivo Gruntfile.js en ese mismo directorio
¡Ahora vamos a ir añadiendo las tareas (grunt tasks)!, quizás te interesen todas las del tutorial o solo una parte.
Tareas de Grunt.js para montarte un entorno de desarrollo web muy PRO
En tu vida diaria de desarrollador web hay infinidad de tareas que puedes automatizar con Gruntjs, en esta entrada vamos a centrarnos en tareas que suele usar un programador web frontend o un diseñador web, no obstante hay tareas aplicables al frontend y al backend como la de guardar los ficheros y que visualices esos cambios en tu navegador sin tener que recargar.
Hay una task esencial que debes instalar: WATCH
Antes de nada instala la task watch, que es una de las tareas más importantes y útiles de Grunt. Lo que hace es «observar los cambios», es decir, ejecuta ciertas tareas cuando hay cambios en los archivos que queremos «observar». Por ejemplo en este código:
1 2 3 4 5 6 |
watch: { sass: { files: "app/scss/*.scss", tasks: ['sass'] } } |
Imagina que tenemos instalado el gruntplugin sass para compilar SASS a CSS, dentro de la task watch le estamos diciendo que cada vez que detecte un cambio en los archivos .scss situados dentro de la carpeta «app/scss/», se ejecute la tarea [‘sass’].
En este ejemplo lo que nos ahorra básicamente es estar escribiendo todo el rato en consola grunt sass para ejecutar la task sass cada vez que hacemos un cambio en el fichero .scss.
¿Has comprendido ya la enorme utilidad de la task watch?, vamos a usarla siempre con el resto de tareas.
Para instalar el grunt plugin watch nos vamos al directorio raíz de nuestro proyecto y escribimos:
1 |
npm install grunt-contrib-watch --save-dev |
Y verás que se ha actualizado el archivo package.json con la nueva dependencia.
Así es como quedaría la task watch añadida a nuestro Gruntfile.js. Por ahora la dejamos vacía:
1 2 3 4 5 6 7 8 9 10 11 12 |
//Gruntfile.js module.exports = function (grunt) { grunt.initConfig({ // Watch task config watch: { }, }); grunt.loadNpmTasks('grunt-contrib-watch'); }; |
Con grunt.loadNpmTasks(‘nombre-de-la-tarea’) cargamos la tarea en el archivo Gruntfile.js
¡Adelante con las tareas!
Recargar automáticamente los cambios de tus ficheros en el navegador con BrowserSync
Cada vez que guardamos un fichero .css, .php, .html, etc, debemos ir al navegador y actualizar de forma manual con F5 o dándole al botón Cargar página de nuevo de Chrome. Vamos a automatizar esto con el gruntplugin BrowserSync.
Primero vamos a la raíz de nuestro proyecto e instalamos el grunt plugin BrowserSync:
1 |
npm install grunt-browser-sync --save-dev |
Una vez instalado abrimos el Gruntfile.js y añadimos la tarea BrowserSync como tal, debemos indicarle dónde están nuestros archivos partiendo de la raíz de nuestro proyecto.
¿Sueles trabajar abriendo los ficheros .html, .php, etc, en tu navegador a pelo o tienes montado un servidor local (con apache y XAMPP por ejemplo) con tu hostname y todo eso?
Lo ideal es que tengas tus proyectos bien montados con un hostname http://tuproyecto.dev o tuproyecto.local, pero por si acaso no es así voy a darte las dos alternativas de BrowserSync.
Siguiendo mi ejemplo del proyecto tutorial-materialize, si trabajara con él abriéndolo de forma estática, mi Gruntfile.js con la task BrowserSync quedaría así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
//Gruntfile.js module.exports = function (grunt) { grunt.initConfig({ // Watch task config watch: { }, // Usamos BrowserSync para tus archivos .html, .css, etc, estáticos. browserSync: { default_options: { bsFiles: { src: [ "css/*.css", "js/*.js", "*.html" ] }, options: { watchTask: true, server: { baseDir: "./" } } } } }); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-browser-sync'); //Tarea por defecto grunt.registerTask('default', ['browserSync', 'watch']); }; |
Ahora bien, si tienes montado un buen servidor local PHP o algo parecido (en mi caso XAMPP) , con un hostname creado, necesitarás usar el modo proxy de Browser Sync. Donde indicas la url de tu sitio:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//Gruntfile.js module.exports = function (grunt) { grunt.initConfig({ // Watch task config watch: { }, // Usamos BrowserSync para tus archivos .html, .css, etc, y además le metemos un proxy de nuestro hostname browserSync: { default_options: { bsFiles: { src: [ "css/*.css", "js/*.js", "*.html" ] }, options: { watchTask: true, proxy: "tuhostname.local" } } } }); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-browser-sync'); //Tarea por defecto grunt.registerTask('default', ['browserSync', 'watch']); }; |
Te habrás fijado que al final he definido una tarea por defecto con el método registerTask, de esta forma ejecutamos tareas distintas en un solo comando, especifico que tareas voy a ejecutar al escribir en consola el comando ‘grunt‘ sin argumentos.
Dicho esto, vamos a la consola y ejecutamos el comando grunt, que hemos definido como tarea por defecto:
1 |
grunt |

Por defecto se ejecuta en el puerto 3000
Se te abrirá una nueva pestaña en tu navegador con el localhost:3000 por defecto, mostrando tu web.
Ahora puedes cambiar cualquier fichero y guardarlo, ¡verás los cambios instantáneamente en tu navegador sin necesidad de recargar!
Truquito para desarrolladores backend: BrowserSync reconoce los cambios en cualquier fichero, no está limitado a inyectar CSS, yo mismo lo uso con Laravel 5. La configuración que tengo para acceder a todas las carpetas es esta:
12345 src: ['app/**/*','public/**/*','resources/views/**/*']
Sincronizar tu navegador con el móvil, tablet y todos los dispositivos que quieras
Esto en realidad ya lo hemos hecho al montarnos la tarea de Browser Sync, fíjate que al ejecutarla se nos muestra en consola una url Local y otra External, pues bien, ve a tu navegador móvil o tablet e introduce la url External.
En mi caso es http://192.168.1.129:3000
¡Pues usar tantos dispositivos como quieras y sincronizar navegadores!, verás que cuando haces scroll en uno se aplica a todos. También te simula los clicks y por supuesto, cada vez que cambies estilos o cualquier tipo de fichero se actualizarán todos los navegadores a la vez.

Un buen entorno de desarrollador web con BrowserSync. Queda bastante pro, digno de la Batcueva.
*Soluciones a posibles problemas para acceder la URL External desde otros dispositivos:
Si al acceder a la URL External te aparece esto en tu navegador Chrome de Android: ERR_ADDRESS_UNREACHABLE. Puede deberse a dos cosas:
a) Tu Firewall está bloqueando el acceso a través de otros dispositivos, modifica la configuración de tu Firewall para dar acceso al puerto 3000.
b) Si a pesar de tener el Firewall desactivado o configurado para que permita la URL External, te sigue fallando, lo que debes hacer es abrir el puerto 3000 desde tu router. Para ello accede al login de tu router con un usuario y contraseña. Debes introducir una dirección en tu navegador (depende la compañía que tengas contratada). En mi caso es Jazztel y el acceso al router es por esta URL http://192.168.1.1/. En cuanto al login y contraseña casi siempre suelen ser:
- admin/1234
- admin/admin (esta me funcionó a mí)
- 1234/1234
- 1234/admin
- root/12345
Una vez logueado en el panel de administración de tu router, haz click en Advanced Setup > NAT > Virtual Servers, y pulsa en el botón Add. Rellena los datos de la imagen de abajo (cambia mi IP asignada por la tuya) y pulsa en Apply/Save:

Habilitamos el puerto 3000 para poder acceder a él desde fuera.
Listo, ya tienes el puerto 3000 configurado, espero que ya puedas disfrutar de toda la potencia de BrowserSync para el desarrollo paginas de web.
Compilar automáticamente de SASS a CSS al guardar los archivos .scss con Grunt Sass
Doy por hecho que cada vez escribes más Sass por sus claras ventajas en el diseño web modular, frente a escribir css. Si no es así puedes saltarte esta task, o bien si sientes curiosidad por Sass puedes echarle un vistazo a este post.
Vamos a automatizar la compilación de los ficheros .scss a .css, para ello instalamos la tarea grunt–contrib–sass con este comando en consola:
1 2 |
//instalamos el gruntplugin sass npm install grunt-contrib-sass --save-dev |
Ahora necesitamos decirle a Grunt dónde encontrar tus ficheros y dejar preparada la task sass. Así es como quedaría el fichero Grunt.js con la task Sass añadida:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
//Gruntfile.js module.exports = function (grunt) { grunt.initConfig({ // Watch task config con Sass watch: { sass: { files: "scss/*.scss", tasks: ['sass'] } }, // Sass task config sass: { dev: { files: { // fichero destino // fichero .scss "css/custom.css" : "scss/custom.scss" } } }, // BrowserSync task config browserSync: { default_options: { bsFiles: { src: [ "css/*.css", "js/*.js", "*.html" ] }, options: { watchTask: true, proxy: "tutorialmaterialize.dev" } } } }); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-browser-sync'); //Tarea por defecto grunt.registerTask('default', ['browserSync', 'watch']); }; |
La configuración de arriba está hecha asumiendo que tienes una estructura de directorios como esta:
1 2 3 |
index.html css/custom.css scss/custom.scss |
Fíjate que debemos cargar el gruntplugin sass con loadNpmTasks junto a las otras tareas. A la tarea por defecto de abajo no tenemos por qué añadirlo ya que el gruntplugin watch está constantemente «vigilando» esos cambios que hagamos y éste ya lo tenemos añadido en la tarea por defecto.
Inicia otra vez la tarea por defecto con el comando grunt. Ahora cada vez que editemos nuestro fichero .scss se ejecutará nuestra tarea sass y se compilará al fichero .css en la ruta especificada, por supuesto todo esto de lo mantiene sincronizado en tus pantallas con la tarea Browser Sync anterior:

Ya tenemos dos tareas de Grunt: Sass y BrowserSync
Poco a poco irás viendo que aprender desarrollo web ágil es de lo más sencillo con Grunt, ¡genial!, vamos con la siguiente tarea.
Borrar el CSS que no uses en tu proyecto con el grunt plugin UnCSS
Como desarrollador web te habrás dado cuenta que las prisas nos hacen dejar rastros de clases y otros selectores css en nuestro proyecto que no usamos. También puede darse el caso de que uses frameworks como Boostrap o Foundation, de los cuales los desarrolladores web suelen emplear menos del 10% de sus estilos.
Al final todo esto se traduce a archivos css pesados que podemos reducir considerablemente gracias a esta tarea: grunt uncss.
Como siempre, primero debemos instalar esta task, vamos desde la consola o terminal a la raíz de nuestro proyecto y escribimos:
1 |
npm install grunt-uncss --save-dev |
El ejemplo sobre el Tutorial Materialize CSS que estamos usando nos viene genial, ya que materialize css es un framework y nos sobran muchísimos elementos de la hoja de estilos que no utilizamos.
Nuestro Gruntfile.js con esta task añadida quedaría así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
//Gruntfile.js module.exports = function (grunt) { grunt.initConfig({ // Watch task config con Sass watch: { sass: { files: "scss/*.scss", tasks: ['sass'] } }, // Sass task config sass: { dev: { files: { // fichero destino // fichero .scss "css/custom.css" : "scss/custom.scss" } } }, // BrowserSync task config browserSync: { default_options: { bsFiles: { src: [ "css/*.css", "js/*.js", "*.html" ] }, options: { watchTask: true, proxy: "tutorialmaterialize.dev" } } }, // UnCSS task config uncss: { dist: { options: { //Estilos que queremos limpiar stylesheets : ['css/materialize.min.css'], //Estilos que no queremos limpiar ignoreSheets: [/custom.css/], }, files: { //Archivo css de salida //Scanea las clases, ids, etc de este html 'css/materialize.min.css': ['index.html'] } } } }); //Cargamos los grunt plugins grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-browser-sync'); grunt.loadNpmTasks('grunt-uncss'); //Tarea por defecto grunt.registerTask('default', ['browserSync', 'watch']); }; |
Fíjate que esta vez no vamos a usar esta task con la task por defecto. He preferido ponerla aparte y ejecutarla manualmente, creo que es algo que haríamos al finalizar el desarrollo y no durante éste.
Le pasamos una serie de parámetros como opción, la hoja de estilos que queremos limpiar y los estilos que queremos ignorar. Luego en files indicamos el nombre del fichero de salida y los archivos .html, .php, etc, que queramos escanear. Puedes ver todas las opciones en la versión de node de esta tarea, valen igualmente su versión en grunt.
Esta tarea también te agrupa todos los ficheros css en uno solo de salida, por eso en mi caso uso la opción ignoreSheets. Solo quería hacerlo con la hoja de estilos materialize.min.css.
Por supuesto también puedes scanear varios ficheros a la vez:
1 2 3 4 5 6 7 |
uncss: { dist: { files: { 'public/css/todasmishojasdeestilos.css': ['app/index.php', 'app/contact.php', 'app/cart.php'] } } } |
También puedes ignorar ciertos selectores y clases que no quieres cargarte por ahora o quizás las añadas al DOM posteriormente por la ejecución de una función javascript en determinadas interacciones:
1 2 3 4 5 6 7 8 9 10 |
uncss: { dist: { options: { ignore: ['#id_que_quiero_mantener', '.clase_que_quiero_conservar'] }, files: { 'public/css/micsslimpio.css': ['app/index.html', 'app/about.html'] } } } |
Repito que si no le pasamos opciones de restricción, este grunt plugin te agrupa todos los css en uno por defecto. En la documentación tienes todo lo necesario.
Como no hemos añadido esto a la tarea grunt por defecto, para ejecutar esta task debemos ir al a consola y escribir:
1 |
grunt uncss |

Fíjate como se reduce el peso del archivo con la task grunt uncss
Pues ya tenemos el fichero limpio, el único problema es que los archivos css que tuvieras minificados (.min.css) se te volverán a descomprimir, pero no te preocupes, ¡vamos a solucionar esto con la siguiente task!
Minificar archivos CSS y JavaScript con grunt cssmin y grunt uglify respectivamente
Minificar archivos consiste en hacerlos menos pesados modificando elementos como espacios innecesarios, tabulaciones, comentarios, etc. Con JavaScript lo que se hace es cambiar el nombre de las variables por otros más breves para ahorrar caracteres. Cuanto más bytes de código nos ahorremos, más mejorará la velocidad de carga de nuestra página y nuestra nota en el Google Page Speed.
Minificar css con cssmin
Usaremos el grunt plugin cssmin. Para instalarlo vamos a la consola y nos situamos en la raíz de nuestro proyecto, escribimos:
1 |
npm install grunt-contrib-cssmin --save-dev |
Lo que vamos hacer es combinar nuestros css en un solo archivo minificado (.min.css). Lo ideal es que trabajes con tus css sin minificar en desarrollo y luego en el despliegue a producción subas todos combinados en un solo archivo .min.css, en este caso lo he llamado allcss.min.css.
Abrimos el Gruntfile.js y primero añadimos esta task cssmin dentro la task watch, ya que nos interesa que cada vez que hagamos un cambio en los archivos css watch lo perciba y llame a la task cssmin para que nos genere inmediatamente el nuestro .css minificado y combinado:
1 2 3 4 5 6 7 8 9 10 11 |
// Watch task config con Sass watch: { sass: { files: "scss/*.scss", tasks: ['sass'] }, cssmin: { files: ["css/*.css","!css/*.min.css"], tasks: ['cssmin'] } }, |
Luego añadimos la task cssmin, aquí tienes su configuración. Le indicamos un fichero de salida y los archivos css que queremos combinar y minificar:
1 2 3 4 5 6 7 8 9 10 11 12 |
// Cssmin task config cssmin: { options: { shorthandCompacting: false, roundingPrecision: -1 }, target: { files: {//Fichero combinado //Ficheros que vamos a combinar, 2 .css 'css/allcss.min.css': ['css/custom.css', 'css/materialize.min.css'] } } } |
Por último ejecuta como siempre la tarea por defecto con el comando grunt y verás que al guardar en cada .css se genera inmediatamente una combinación de todos tus css en un solo fichero ya minificado:

Minificar css con grunt cssmin
Minificar javascript con uglify
Uglify es una task de grunt muy buena para la minificación de javascript. También tiene muchas opciones en la documentación, como combinar tus archivos javascript en un solo archivo, aunque esto debe hacerse con cuidado y lo recomendable es que cada archivo que tengas lo envuelvas en IIFE (Immediately Invoked Function).
Nosotros simplemente vamos a minificar un archivo o archivos Javascript concretos.
Primero instalamos uglify con este comando:
1 |
npm install grunt-contrib-uglify --save-dev |
Luego lo añadimos dentro de la task watch para que detecte cambios en nuestros archivos javascript e ignoramos los ya minificados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Watch task config watch: { sass: { files: "scss/*.scss", tasks: ['sass'] }, cssmin: { files: ["css/*.css","!css/*.min.css"], tasks: ['cssmin'] }, uglify: { files: ["js/*.js", "!js/*.min.js"], tasks: ['uglify'] } }, |
Lo siguiente es añadir la task uglify y su configuración. Indicamos la ruta de donde grunt cogerá los ficheros javascript y la ruta donde los soltará ya con el javascript minificado:
1 2 3 4 5 6 7 |
//Uglify task config uglify: { build: { src: 'js/custom.js',//Ruta de fichero de entrada dest: 'js/custom.min.js'//Ruta del fichero minificado } } |
Por último usa el comando grunt en tu terminal o consola para iniciar las tareas que tenemos definidas, verás que al guardar tus archivos .js se genera el archivo javascript minificado .min.js:

Minificar javascript con uglify
Gruntfile con nuestras dos tareas de minificación añadidas:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
//Gruntfile.js module.exports = function (grunt) { grunt.initConfig({ // Watch task config watch: { sass: { files: "scss/*.scss", tasks: ['sass'] }, cssmin: { files: ["css/*.css","!css/*.min.css"], tasks: ['cssmin'] }, uglify: { files: ["js/*.js", "!js/*.min.js"], tasks: ['uglify'] } }, // Sass task config sass: { dev: { files: { // fichero destino // fichero .scss "css/custom.css" : "scss/custom.scss" } } }, // BrowserSync task config browserSync: { default_options: { bsFiles: { src: [ "css/*.css", "js/*.js", "*.html" ] }, options: { watchTask: true, proxy: "tutorialmaterialize.dev" } } }, // UnCSS task config uncss: { dist: { options: { //Estilos que queremos limpiar stylesheets : ['css/materialize.min.css'], //Estilos que no queremos limpiar ignoreSheets: [/custom.css/], }, files: { //Archivo css de salida //Scanea las clases, ids, etc de este html 'css/materialize.min.css': ['index.html'] } } }, // Cssmin task config cssmin: { options: { shorthandCompacting: false, roundingPrecision: -1 }, target: { files: {//Fichero combinado //Ficheros que vamos a combinar, 2 .css 'css/allcss.min.css': ['css/custom.css', 'css/materialize.min.css'] } } }, //Uglify task config uglify: { build: { src: 'js/custom.js',//Ruta de fichero de entrada dest: 'js/custom.min.js'//Ruta del fichero minificado } } }); //Cargamos los grunt plugins grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-browser-sync'); grunt.loadNpmTasks('grunt-uncss'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-uglify'); //Tarea por defecto grunt.registerTask('default', ['browserSync', 'watch']); }; |
Comprimir imagenes jpg, png, svg y gif con grunt imagemin
Una de las tareas más pesadas es la de comprimir y optimizar las imágenes del proyecto para mejorar los tiempos de carga. Grunt nos ofrece la task imagemin para acelerar este proceso.
Para instalarla nos vamos a la consola y situados en la raíz de nuestro proyecto escribimos:
1 |
npm install grunt-contrib-imagemin --save-dev |
Abre el Gruntfile.js y carga imagemin junto al resto de grunt plugins:
1 |
grunt.loadNpmTasks('grunt-contrib-imagemin'); |
Añade la task imagemin configurada al resto de tareas:
1 2 3 4 5 6 7 8 9 10 11 |
//Imagemin task config imagemin: { main: { files: [{ expand: true, cwd: 'img/', //todas las imágenes de esta ruta src: ['**/*.{png,jpg,gif,.svg}'], //patrón de tipos de imagen dest: 'img/' //carpeta destino una vez optimizadas }] } } |
Y añade imagemin también dentro de la tarea watch para que cada vez que metas una nueva imagen en tu directorio /img se compriman éstas automáticamente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Watch task config watch: { sass: { files: "scss/*.scss", tasks: ['sass'] }, cssmin: { files: ["css/*.css","!css/*.min.css"], tasks: ['cssmin'] }, uglify: { files: ["js/*.js", "!js/*.min.js"], tasks: ['uglify'] }, imagemin: { files: ['img/*.{png,jpg,gif,svg}'], tasks: ['imagemin'], options: { spawn: false, } } }, |
Bien, si ejecutas grunt en consola verás que cada vez que guardes una imagen en el directorio que has indicado se reducirá de peso automáticamente:

Comprimir imagenes con grunt imagemin
¿Pero qué pasa si tenemos muchas imágenes que optimizar?, se comprimirán todas una y otra vez, haciendo que el proceso sea lento y tedioso.
Para evitar que esta task vuelva a optimizar las imágenes ya comprimidas, podemos usar el grunt plugin newer, que se dedica a detectar qué imágenes nuevas se han añadido recientemente a tu carpeta para así trabajar solo con ellas.
Vamos allá:
1 |
npm install grunt-newer --save-dev |
Cargamos la task newer en el Gruntfile.js:
1 |
grunt.loadNpmTasks('grunt-newer'); |
Y no hace falta configurarlo, lo metemos dentro del watch que tenemos nombrado para imagemin:
1 2 3 4 5 6 7 |
imagemin: { files: ['img/*.{png,jpg,gif,svg}'], tasks: ['newer:imagemin'],//añado newer options: { spawn: false, } } |
Se debe anteponer newer a la task imagemin.
Archivo Gruntfile.js final, con todas las tareas añadidas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
//Gruntfile.js module.exports = function (grunt) { grunt.initConfig({ // Watch task config watch: { sass: { files: "scss/*.scss", tasks: ['sass'] }, cssmin: { files: ["css/*.css","!css/*.min.css"], tasks: ['cssmin'] }, uglify: { files: ["js/*.js", "!js/*.min.js"], tasks: ['uglify'] }, imagemin: { files: ['img/*.{png,jpg,gif,svg}'], tasks: ['newer:imagemin'], options: { spawn: false, } } }, // Sass task config sass: { dev: { files: { // fichero destino // fichero .scss "css/custom.css" : "scss/custom.scss" } } }, // BrowserSync task config browserSync: { default_options: { bsFiles: { src: [ "css/*.css", "js/*.js", "*.html" ] }, options: { watchTask: true, proxy: "tutorialmaterialize.dev" } } }, // UnCSS task config uncss: { dist: { options: { //Estilos que queremos limpiar stylesheets : ['css/materialize.min.css'], //Estilos que no queremos limpiar ignoreSheets: [/custom.css/], }, files: { //Archivo css de salida //Scanea las clases, ids, etc de este html 'css/materialize.min.css': ['index.html'] } } }, // Cssmin task config cssmin: { options: { shorthandCompacting: false, roundingPrecision: -1 }, target: { files: {//Fichero combinado //Ficheros que vamos a combinar, 2 .css 'css/allcss.min.css': ['css/custom.css', 'css/materialize.min.css'] } } }, //Uglify task config uglify: { build: { src: 'js/custom.js',//Ruta de fichero de entrada dest: 'js/custom.min.js'//Ruta del fichero minificado } }, //Imagemin task config imagemin: { main: { files: [{ expand: true, cwd: 'img/', //todas las imágenes de esta ruta src: ['**/*.{png,jpg,gif,.svg}'], //patrón de tipos de imagen dest: 'img/' //carpeta destino una vez optimizadas }] } } }); //Cargamos los grunt plugins grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-browser-sync'); grunt.loadNpmTasks('grunt-uncss'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-imagemin'); grunt.loadNpmTasks('grunt-newer'); //Tarea por defecto grunt.registerTask('default', ['browserSync', 'watch']); }; |
Package.json final, con todas las dependencias necesarias
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "name": "tuproyecto", "version": "1.0.0", "description": "", "author": "tumismo", "devDependencies": { "grunt": "^0.4.5", "grunt-browser-sync": "^2.1.3", "grunt-contrib-cssmin": "^0.13.0", "grunt-contrib-imagemin": "^0.9.4", "grunt-contrib-sass": "^0.9.2", "grunt-contrib-uglify": "^0.9.1", "grunt-contrib-watch": "^0.6.1", "grunt-newer": "^1.1.1", "grunt-uncss": "^0.4.3" } } |
Instalación de todas las task en cualquier proyecto de forma rápida
Si quieres ir directo al grano ve a tu proyecto haz el npm init y crea el package.json, a ese package.json debes añadirle el siguiente fragmento con las dependencias:
1 2 3 4 5 6 7 8 9 10 11 |
"devDependencies": { "grunt": "^0.4.5", "grunt-browser-sync": "^2.1.3", "grunt-contrib-cssmin": "^0.13.0", "grunt-contrib-imagemin": "^0.9.4", "grunt-contrib-sass": "^0.9.2", "grunt-contrib-uglify": "^0.9.1", "grunt-contrib-watch": "^0.6.1", "grunt-newer": "^1.1.1", "grunt-uncss": "^0.4.3" } |
Luego copia el Gruntfile.js completo que tienes más arriba y modifica la ruta de las carpetas, etc, a tu gusto.
Por último haz un npm install en consola y node te instalará grunt y todas las dependencias que hay en tu package.json.
¡Se acabó!
Conclusiones finales
Lo más seguro es que no tengas la necesidad de usar las 6 automatizaciones de este tutorial, a la gente le suele encantar BrowserSync, ya que es la más vistosa y la que más alucinado te deja.
Pero en el desarrollo de páginas web, sobre todo a la hora de optimizar nuestras webs para el Google Page Speed, nos vienen genial también las tareas de minificar css, javascript y comprimir imagenes.
Como desarrollador web he intentado buscar un equilibrio con Grunt y estas tareas, esta selección de tareas grunt es meramente personal.
Al fin de cuentas, es tu entorno de desarrollo web, tú decides como montártelo ;).
No olvides comentar si tienes algún fallo en la instalación o alguna duda, ¡comparte si te ha sido útil!
Buenísimo!! Para cuando uno con gulp??
Pues pronto, porque he visto que tiene una sintaxis más sencilla que Grunt.
Excelente desde ahora sere tu FAN eres realmente bueno, me gustaria que se pueda hacer eso pero desde el servidor es decir hosting de paga, es decir en sublime text 3 hago cambios y automáticamente aparece en la web alojada en el servidor, utilizo el archivo stfp.json que se encarga de conectarme a la base de datos y realizar el cambio cada que le doy en guardar, pero browser sync es realmemnte bueno puedes hacer un tutorial donde esto lo podamos hacer ya directamente desde el servdor de paga.
Hola Gerardo!, gracias por el comentario, a ver he investigado un poco lo que comentas y no veo que el browser sync que trae grunt pueda gestionar eso. Sin embargo puedes echar un vistazo al auténtico Browser Sync: http://www.browsersync.io/docs/options/
Entre su documentación quizás encuentres lo que buscas.
Un saludo.
Genial!!!! hacia un monton que queria borbar browsersync
Muchas gracias!!!!!!!
Hay una forma de cambiar el localhost:3000 por uno predeterminado? y como podría hacerlo?
Saludos
Sí, mira esta parte de la documentación: https://www.browsersync.io/docs/options/#option-port
Simplemente añádelo en las opciones:
options: {
….
port: 8010,
}
Amigo muy buen tutorial, la información es muy precisa, gracias. Ahora hay algo que me surgió así que seria un gusto si pudieras ayudarme y es simple cuando hago todo para el browser sync, después cuando voy a algún archivo y lo modifico a travez de sublime text 3, no pasa nada en el navegador, me sigue tocando actualizar manualmente para ver los cambios, sera que tienes idea de alguna falla?? Gracias por la ayuda.
Hola amigo muy buen tutorial, soy un noob todavía en esto pero ya que se ha hecho todo eso me gustaría que hicieras un tutorial donde además de lo visto anteriormente también se le agregue unos archivos .html y se les cargue las librerias de Materialize para el diseño y también se le cargue angular para poder correr nuestro proyecto con grunt y se vea con buen diseño de materialize, gracias por tu info.
Excelente trabajo muy bien explicado, muchas gracias he aprendido mucho.
Voy a probarlo, suena genial. Muchas gracias por la buena explicación, de acá saque mucha más info que no tenía.
Saludos