¡Hola! Me parece que andas usando un bloqueador de anuncios =( ¿Nos desbloqueas? ¿Por qué?
F13

Funcion 13

Introducción a Webpack 2

Desde que empecé en serio con JavaScript, hemos tenido un puñado de gestores de ficheros y sistemas para compilar nuestros archivos JavaScript o Sass. Grunt, Gulp, Broccoli por nombrar algunos.

Webpack es algo parecido aunque deja las tareas de lado como pronto veremos. Tareas que iremos ejecutando con node, sin más.

¿Qué es Webpack?

Hay mucha gente que se empeña en comparar Webpack con gestores de tareas como Grunt o Gulp pero Webpack no es más que una especie de empaquetador de módulos para JavaScript aunque poco a poco ha ido evolucionando para ayudarte con todos los ficheros del lado front-end.

Si miramos Gulp por ejemplo, puede usar multitud de pre-procesadores y transpiladores pero si extraemos su esencia observamos que Gulp toma una fuente y la compila en una salida. Esto lo hace caso por caso sin tener en cuenta el sistema en sí. Es tarea del programador el encargarse desde donde lo deja Gulp para asegurarse de que todas las partes móviles encajen en un entorno de producción.

Webpack intenta aligerar la carga del programador un poco haciendo una pregunta un tanto presuntuosa: ¿Y si hay una parte del proceso de desarrollo que gestione las dependencias por sí mismo? ¿Qué pasaría si pudiéramos escribir código de manera que el proceso de construcción se gestionara solo, basado en la necesidad final?

Webpack intenta facilitar el proceso de construcción pasando las dependencias a través de JavaScript.

¿Alguna vez te has preocupado por alguna de estas cosas?

  • Cargar dependencias en el orden incorrecto.
  • Has incluído CSS o JS que no se usa en producción.
  • Librerías que accidentalmente se cargan más de una vez.
  • Encontrar un buen sistema para gestionar dependencias de Node/Bower en JavaScript.
  • Optimizar los ficheros del proyecto pero te preocupa que rompas algo.

Entonces déjame decirte que Webpack te puede servir de ayuda.

Webpack 2

Webpack lleva ya un tiempo con nosotros pero desde Febrero de 2017 se actualizó a la versión 2 que, principalmente ofrece más velocidad a la hora de compilar los recursos.

Es importante destacar que esta serie de tutoriales es para la versión 2 y que no funcionarán para la versión 1.

Primeros pasos

Esta es la estructura que vamos a usar:

├── index.html
└── src
    ├── index.js
    └── modules
        └── nombre.js

El fichero nombre.js contiene esto:

export default function() {  
  return 'Funcion13';
}

Mientras que index.js contiene esto:

import nombre from './modules/nombre';

console.log(nombre());  

Como ves, no hay nada raro por aquí. Esto es solo para comprobar que import/export funcionan correctamente. Necesitamos tener instalado Node y, por supuesto, npm.

Nota: Es posible que todo esto de Node.js te suene pero si quieres saber más y aprender, te recomiendo que leas mi libro Descubriendo Node.js y Express.

Ahora vamos a crear nuestro fichero package.json si es que no lo tenemos creado ya:

$ npm init

Vamos a instalar todas las dependencias que necesitamos:

  • babel-cli:
  • babel-core
  • babel-loader
  • babel-preset-es2015
  • webpack
  • webpack-dev-server
  • rimraf

Babel es la herramienta para transpilar nuestro código ES6 a ES5. No tengo que explicar lo que es Webpack, ¿no? Y rimraf es una pequeña herramienta para poder borrar directorios y ficheros desde node de forma multiplataforma de manera que funcione en Windows y en Mac. Si el proyecto no necesita ser compilado en distintos sistemas os podéis ahorrar la dependencia.

Para instalarlo todo podemos ejecutar esta línea:

npm install babel-cli babel-core babel-loader babel-preset-es2015 rimraf webpack webpack-dev-server --save-dev  

Instalaremos todas las dependencias y las guardaremos en nuestro archivo package.json que ahora debería ser algo así:

{
  "name": "es6",
  "devDependencies": {
    "babel-cli": "^6.24.1",
    "babel-core": "^6.24.1",
    "babel-loader": "^7.0.0",
    "babel-preset-es2015": "^6.24.1",
    "rimraf": "^2.6.1",
    "webpack": "^2.4.1",
    "webpack-dev-server": "^2.4.3"
  }
}

Ahora que ya tenemos todo esto hecho, necesitamos crear un fichero webpack.config.js en la raíz de nuestro directorio con este contenido:

const path = require('path');

const src = path.join(__dirname, 'src');

module.exports = {  
  context: src,
  entry: {
    app: './index.js'
  },
  output: {
    filename: '[name].js',
    path: path.join(__dirname, 'dist/js'),
    publicPath: '/dist/js/'
  },
  devServer: {
    contentBase: __dirname
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: src
      }
    ]
  }
};

Vamos a ver lo que le estamos aquí diciendo a webpack.

  1. Comienza en el contexto, que es donde ubicaremos nuestros ficheros JS.
  2. Busca ficheros de entrada (index.js) y lo nombras como te digamos app.
  3. Ahora lee el contenido (busca los imports) y parsea el código.
  4. El fichero de salida quiero que se llame como te indicamos antes (app) .js. Si colocáramos '[name].f13.js' se llamaría app.f13.js. Me lo vas a colocar en dist/js que va a ser accesible por el servidor en /dist/js/.
  5. El servidor de webpack (devServer) va a servir desde la raíz.
  6. Todos los ficheros js los vas a pasar por babel-loader que básicamente es un puente entre webpack y Babel.

Ahora que ya tenemos todo esto vamos a crear un script en nuestro package.json para echarlo a andar:

{
  "name": "es6",
  "devDependencies": {
    "babel-cli": "^6.24.1",
    "babel-core": "^6.24.1",
    "babel-loader": "^7.0.0",
    "babel-preset-es2015": "^6.24.1",
    "npm-run-all": "^4.0.2",
    "rimraf": "^2.6.1",
    "webpack": "^2.4.1",
    "webpack-dev-server": "^2.4.3"
  },
  "scripts": {
    "build": "webpack"
  }
}

Nota: Si quieres saber más sobre los scripts en Node, no dejes de leer Ejecutando scripts con NPM.

Si ahora ejecutamos npm run build deberíamos ver algo así en el terminal:

$ npm run build

> es6@ build /Users/alaguna/Projects/es6-import-export
> webpack

Hash: 43b031f573bca5884be3  
Version: webpack 2.4.1  
Time: 120ms  
 Asset     Size  Chunks             Chunk Names
app.js  3.17 kB       0  [emitted]  app  
   [0] ./modules/nombre.js 52 bytes {0} [built]
   [1] ./index.js 62 bytes {0} [built]

Y tendríamos que tener un nuevo fichero en /dist/js/ llamado app.js. El contenido es un poco raro aunque si nos paramos un poco a leer, veremos que el código que escribimos está ahí y si lo ejecutamos veremos que funciona correctamente.

Añadiendo librerías

Lo habitual en el desarrollo es usar algunas librerías que nos interesen y que, al compilarlo todo, las librerías estén incluídas en el fichero final. Vamos a instalar un par de librerías, moment y jQuery:

npm install moment jquery --save  

Las guardamos porque ahora indicamos que forman parte del proyecto, de manera que cualquiera que se una a él, obtiene todas las dependencias necesarias al ejecutar npm install.

Ahora vamos a cambiar el contenido de index.js para añadir nuestras nuevas librerías:

import moment from 'moment';  
import jQuery from 'jquery';  
import nombre from './modules/nombre';

const ahora = moment().format('DD/MM/YYYY - HH:mm:ss');  
const $body = jQuery('body');

console.log(ahora);  
console.log(nombre());  

Como ves es cuestión únicamente de llamarlas por nombre y podremos importarlas normalmente. La única diferencia radica en que las librerías no necesitan ninguna ruta relativa, simplemente usamos el nombre y ya podemos acceder a ellas.

Si ejecutáramos este código, obtendríamos un resultado parecido a este:

22/04/2017 - 23:25:00  
Funcion13  

¡Todo correcto!

Minificando el código

Tal y como tenemos configurado Webpack ahora mismo, el código que genera Webpack no está minificado. Es bueno para el desarrollo pero un derroche de recursos en producción.

Webpack puede usar una opción -p (p de producción) que se encarga de quitar todos los espacios libres, quitar comentarios, etc:

{
  "name": "es6",
  "devDependencies": {
    "babel-cli": "^6.24.1",
    "babel-core": "^6.24.1",
    "babel-loader": "^7.0.0",
    "babel-preset-es2015": "^6.24.1",
    "npm-run-all": "^4.0.2",
    "rimraf": "^2.6.1",
    "webpack": "^2.4.1",
    "webpack-dev-server": "^2.4.3"
  },
  "scripts": {
    "build": "webpack"
    "min": "webpack --output-filename [name].min.js -p"
  }
}

Como ves, estamos pasando la opción antes indicada y además estamos sobrescribiendo la opción output.filename que, recordemos, era [name].js. Al ejecutar ahora npm run min veremos como se genera un fichero app.min.js y que está minificado.

Usando el servidor de Webpack

Webpack tiene un servidor web incorporado que es realmente útil y práctico. Si recuerdas, añadimos parte de su configuración antes así que vamos a terminar de configurarlo para poder usarlo.

Primero vamos a crear nuestro fichero HTML, muy básico:

<html>  
<head>  
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Aprendiendo Webpack</title>
</head>  
<body>  
  <script src="/dist/js/app.js"></script>
</body>  
</html>  

Si nos fijamos bien, la ruta de nuestro fichero coincide con el publicPath en nuestro fichero webpack.config.babel.js. Ahora vamos a crear nuestro script. Para ello vamos a añadir un script que incluya webpack-dev-server. Nuestro fichero package.json debería quedar ahora así:

{
  "name": "es6",
  "devDependencies": {
    "babel-cli": "^6.24.1",
    "babel-core": "^6.24.1",
    "babel-loader": "^7.0.0",
    "babel-preset-es2015": "^6.24.1",
    "npm-run-all": "^4.0.2",
    "rimraf": "^2.6.1",
    "webpack": "^2.4.1",
    "webpack-dev-server": "^2.4.3"
  },
  "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server"
  },
  "dependencies": {
    "jquery": "^3.2.1",
    "moment": "^2.18.1"
  }
}

Si ahora ejecutamos npm run dev, vamos a ver que aparece en consola mucha información pero lo que nos interesa es lo siguiente:

Project is running at http://localhost:8080/  
webpack output is served from /dist/js/  
Content not from webpack is served from /Users/alaguna/Projects/es6-import-export  

Webpack nos está diciendo que está sirviendo el contenido en el puerto 8080 de nuestro localhost y que además, los ficheros js los va a servir desde /dist/js/ (nuevamente el directorio de publicPath) y que el resto del contenido lo sirve desde la carpeta en la que está el proyecto (esto es lo que indicamos cuando le dijimos contentBase: __dirname).

Ahora si abrimos http://localhost:8080/ en nuestro navegador y miramos la consola, veremos que todo funciona correctamente. Si cambiamos el contenido del fichero JavaScript y guardamos, verás que el navegador se recarga automáticamente para actualizar los cambios. ¡Realmente práctico!

Conclusión

Webpack puede intimidar un poco especialmente con su configuración pero una vez está todo funcionando, podemos disfrutar y comenzar a programar usando módulos y toda la sintaxis de ES6 sin preocuparnos de nada más.

Webpack tiene, además, otros muchos usos que iremos viendo poco a poco en futuros artículos.

¿Tienes alguna duda? ¿Te has quedado atascado? ¿Algo que quieras ver mejor explicado? ¿Algo de Webpack que te gustaría ver? ¡Házmelo saber en los comentarios!

Programador Front-end en First + Third y Potato. Trabajando con JavaScript y HTML5 desde el corazón de Sevilla.

Comentarios ¡Únete a la conversación!