Webpack: Gestión integrada y eficiente de tus assets

webpack es una herramienta de agregación de recursos para aplicaciones web que deja producir una distribución única desde un conjunto establecido de assets.

Entre los recursos que es capaz de administrar webpack, podemos hallar formatos soportados por defecto como HTML, JavaScript, CSS, tal como otros formatos que precisan ser transformados como SASS, CoffeeScript o bien Jade.

Cuando trabajamos en un proyecto web, en un modo de funcionamiento más tradicional, es frecuente partir de un archivo HTML con archivos JavaScript y CSS relacionados a través de los tags script, enlace o bien style.

El inconveniente acostumbra a ser que, conforme las dependencias medran y el desarrollo se dificulta, aumenta el número de assets a ser incluidos en el HTML y con esto el número de solicitudes que el navegador debe efectuar al servidor para cargar la página, haciendo que el desempeño se vea perjudicado ostensiblemente.

Con webpack definiremos un solo pipeline de procesamiento de recursos, consiguiendo un solo archivo embalado y minificado con todos y cada uno de los recursos precisos para nuestro desarrollo.

En este aritículo examinaremos a fondo webpack, centrándonos en los primordiales casos de empleo con los que nos podemos hallar.

Instalación

webpack usa NodeJS, con lo que debemos poder ejecutar anteriormente (cualquier versión actual de NodeJS servirá):

dólares americanos node -v
v5.10.1

Por otro lado, vamos a instalar webpack a través de NPM, con lo que asimismo lo vamos a deber tenerlo operativo:

dólares americanos npm -v
tres.8.3

Con NodeJS activo y marchando, ya podemos instalar webpack para su empleo on line de comandos:

dólares americanos npm install webpack -g

NOTA: Para utilizar una dependencia en la sección de scripts del package.json, no es preciso instalar esta dependencia como global con -g. En un caso así, con un npm install usual es suficiente al enlazarse siempre y en todo momento las dependencias en el node_modules/.bin.

Otro punto esencial es que instalar un bulto como webpack de forma global hace que no tengamos un excesivo control sobre la versión que estamos usando. Si suprimimos el flag -g, webpack se instalará en nuestro directorio de trabajo y deberemos ejecutarlo de la próxima manera:

dólares americanos node_modules/.bin/webpack

Creación de un proyecto simple

Antes de seguir, iniciamos el proyecto:

dólares americanos mkdir webpack-simple
dólares americanos cd webpack-simple
dólares americanos npm init -y

NOTA: Como recomendación, es interesante agregar al package.json el atributo private: true a fin de que este módulo jamás pueda ser subido a un repositorio NPM público.

Ahora, debemos instalar la dependencia de webpack en el proyecto con:

dólares americanos npm install webpack –save-dev

NOTA: Si eres un fan de la línea de comandos, recuerda que NPM tiene un modo abreviado para lanzar instrucciones. Por servirnos de un ejemplo, el comando precedente podría simplicarse de la próxima forma: npm i webpack -D

Para nuestro primer ejemplo simple, crearemos un archivo aplicación.js con un contenido sencillo:

console.log('Webpack rocks!!');

Y un index.html desde el que se incluya el resultado de ejecutar webpack sobre estos assets:

Si ahora ejecutamos webpack on-line de comandos, vamos a poder producir el archivo bundle.js y ya vamos a poder cargar index.html en nuestro navegador y revisar la consola para poder ver el mensaje que hemos generado:

dólares americanos webpack aplicación.js bundle.js

Finalmente, podemos registrar como script en NPM el comando que terminamos de ejecutar on-line de comandos, con la intención de facilitar el acceso al mismo, quedando el package.json como sigue:

undefined

Por último, ya podemos ejecutar el nuevo script de una manera sencilla:

dólares americanos npm run build
Hash: 05a0258fd3b2db7cabe0
Version: webpack ciento doce
Time: 30ms
Asset Size Chunks Chunk Names
bundle.js ciento cuarenta y dos kB cero [emitted] main
[0] ./app.js treinta y dos bytes undefined [built]

Uso de módulos

NodeJS incorpora un sistema de definición y también importación de módulos conocido como CommonJS. De forma muy resumida, CommonJS deja que podamos exportar funciones y objetos complejos desde un archivo JavaScript o bien módulo, para entonces importarlos y poder de esta forma reusarlos donde sea preciso a través de el empleo de exports y require.

Así, podemos tener un archivo messenger.js con el próximo contenido y exportar la función hola a través de module.exports:

module.exports = function hola(valor) undefined

Así, podemos emplear más tarde este módulo y la función exportada desde nuestra aplicación primordial aplicación.js a través de require:

var saluda = require('./messenger.js');
saluda();

Como comentaba al comienzo del apartado, este mecanismo está soportado por NodeJS, mas no tenemos una forma nativa de usar CommonJS en los navegadores (cuando menos hasta el momento en que todos incorporen ES6 modules).

Para lograr esta modularización y encapsulación de nuestro código, podemos decantarse por usar el Module Pattern o bien delegar en herramientas como webpack o bien browserify a fin de que nos provean de esta funcionalidad en el navegador.

Para ponerlo en práctica, vamos a crear un nuevo proyecto llamado webpack-modules siguiendo el proceso de configuración del ejemplo precedente (crear directorios, npm init y también instalar dependencia de webpack).

Ahora, aplicaremos los cambios descritos (cambiamos el aplicación.js y creamos el messenger.js) y ejecutaremos npm start para lanzar webpack y producir de esta forma el resultado final:

dólares americanos npm run build
Hash: c100270751e8d3b6a8d7
Version: webpack ciento doce
Time: 37ms
Asset Size Chunks Chunk Names
bundle.js ciento cincuenta y nueve kB cero [emitted] main
[0] ./app.js cincuenta bytes undefined [built]
[1] ./messenger.js setenta y dos bytes undefined [built]

Si ahora abrimos el archivo index.html con el navegador, vamos a ver exactamente el mismo resultado de ya antes por consola, mas esta vez la lógica ejecutada ha sido importada desde el módulo pertinente.

Si tienes curiosidad por ver como webpack ha obrado el milagro de los módulos, edita el archivo generado bundle.js y vas a ver todo el montaje que hace falta :)

Gestión de la configuración

Como hemos visto, es posible llamar al comando webpack con los factores precisos para efectuar el procesamiento. En todo caso, conforme ajustemos su funcionamiento con más opciones, va a ser considerablemente más fácil delimitar todas y cada una de las configuraciones en un archivo único que webpack sea capaz de cargar cuando lo lancemos.

Este archivo de configuración se acostumbra a llamar webpack.config.js y una definición básica del mismo puede ser:

module.exports = undefined

Con este archivo creado en nuestro directorio de proyecto, vamos a poder ejecutar webpack sin pasarle ninguna opción y mismo va a cargar automaticamente el archivo de configuración con las opciones definidas.

Loaders: Hojas de estilo CSS

Entre las premisas que prosigue webpack es que cualquier recurso empleado en la aplicación debe definirse y ser tratado como un módulo, con independencia de si es un archivo JavaScript, CSS o bien cualquier otro recurso. Ya se que suena un tanto extraño, mas es más simple de lo que semeja :)

Esto desea decir que, si deseamos que nuestros estilos CSS sean minificados y también incluidos en la distrubición generada, deberemos hacer un require de estos archivos desde el punto de entrada primordial (aplicación.js en nuestro caso) o bien incluirlos como entry point (lo mismo va a pasar si deseamos incluir alguna imagen, por servirnos de un ejemplo).

De este modo, webpack los procesará y dejará su aplicación y carga desde JavaScript de manera directa, eludiendo el tener referenciados los archivos CSS a través de enlace o bien style en la página HTML primordial y minimizando de este modo el número de solicitudes que se hacen al servidor para llenar la carga de la página.

Veremos como marcha verdaderamente!!

Partiendo de nuestro ejemplo de empleo de módulos webpack-modules, crearemos un proyecto nuevo llamado webpack-css (bien sabes, hay que iniciar el proyecto como en todos y cada uno de los casos precedentes).

Para este proyecto, vamos a procurar incluir en el pipeline de procesamiento de webpack el tratamiento de los estilos CSS que hemos definido en el archivo style.css:

body undefined

Para esto y si todo en webpack es un módulo, deberemos alterar el aplicación.js y hacer un require de nuestro archivo CSS. Sí, has oido bien, un require de CSS:

require('./style.css');

var saluda = require('./messenger.js');
saluda();

Es bastante curioso, mas así va a procurar procesar este módulo a través de entre los loaders que tenga cargados. Como en un inicio solo contamos con un loader para archivos JavaScript que viene por defecto, si tras esta modificación procuramos ejecutar el comando webpack, se generará un fallo indicándonos esta situación:

Hash: b07e232caa73ce9c11c0
Version: webpack ciento doce
Time: 46ms
Asset Size Chunks Chunk Names
bundle.js ciento setenta y cinco kB cero [emitted] main
[0] ./app.js setenta y cinco bytes undefined [built] [1 error]
[1] ./style.css cero bytes [built] [failed]
[2] ./messenger.js setenta y dos bytes undefined [built]

ERROR in ./style.css
Module parse failed: /base/webpack/webpack-css/style.css Line 1: Unexpected token {
You may need an appropriate loader to handle this archivo type.
| body {
| background-color: #ddd;
| color: red;
@ ./app.js 1:0-veintidos

Para solventar el inconveniente, precisamos hacer 2 cosas. La primera es instalar el loader adecuado usando NPM:

npm install css-loader style-loader –save-dev

Y la segunda es configurar webpack para cargar este módulo de procesamiento CSS cuando procese nuestro pipeline.

Como hemos visto en el apartado precedente, esto lo vamos a poder hacer alterando el archivo webpack.config.js, el que va a quedar de la próxima forma:

module.exports = undefined

NOTA: Cuando veamos en un loader más de un término separados por una admiración, comprenderemos que se marchan a aplicar múltiples loaders encadenados comenenzando siempre y en toda circunstancia de derecha a izquierda.

Si ejecutamos nuevamente el comando webpack, el resultado va a ser correcto:

dólares americanos webpack
Hash: 1a274dc84f3f29fd250a
Version: webpack ciento doce
Time: 352ms
Asset Size Chunks Chunk Names
bundle.js ciento siete kB cero [emitted] main
[0] ./app.js setenta y cinco bytes undefined [built]
[5] ./messenger.js setenta y dos bytes undefined [built]
cuatro hidden modules

Si deseamos ver en detalle todos y cada uno de los módulos intermedios que ha debido cargar webpack para llenar la labor de incluir nuestro CSS, deberemos incluir el paràmetro –display-modules:

dólares americanos webpack –display-modules
Hash: 1a274dc84f3f29fd250a
Version: webpack ciento doce
Time: 274ms
Asset Size Chunks Chunk Names
bundle.js ciento siete kB cero [emitted] main
[0] ./app.js setenta y cinco bytes undefined [built]
[1] ./style.css ochocientos noventa y cinco bytes undefined [built]
[2] ./~/css-loader!./style.css doscientos ocho bytes undefined [built]
[3] ./~/css-loader/lib/css-base.js ciento cincuenta y uno kB undefined [built]
[4] ./~/style-loader/addStyles.js seiscientos nueve kB undefined [built]
[5] ./messenger.js setenta y dos bytes undefined [built]

No estaría de más comprobar el contenido del bundle.js. Ahora vamos a poder ver que hay considerablemente más código y que hay una sección dedicada a cargar nuestros estilos CSS y aplicarlos.

Hojas de estilo externas

Por lo general, entremezclar 2 géneros de outputs como son el procesamiento de JavaScript y el CSS, no es totalmente deseable, en tanto que es esencial favorecer el cacheo de los recursos en el navagador y si mandamos todos y cada uno de los recursos juntos, este archivo completo va a mudar considerablemente más veces y se va a deber descargar completo cada vez.

En el ejemplo precedente, si lo que deseamos es que los estilos se produzcan un archivo .css separado, solo deberemos utilizar un complemento para webpack llamado extract-text-webpack-complemento. Este complemento deja advertir