Tercera (¡y última!) parte de esta serie, donde aprenderemos cómo establecer un development server, entre otros temas útiles e interesantes. Hasta ahora hemos aprendido las bases de Babel y Wepack en los episodios #1 y #2. Si acabas de aterrizar aquí, te sugiero que veas esas partes primero. Dicho esto, ¡vamos al lío!
Cómo usar el servidor local de desarrollo de Webpack
Como mencioné en la parte #1, uso live-server (un plugin de vsCode) para ver mis cambios en el navegador de manera automática cuando guardo (Ctrl+S). Pero no lo vamos a necesitar más, porque vamos a configurar un servidor local de desarrollo (en inglés, un local dev server) con el que cuenta Webpack.
Ese local server de Webpack nos evitará el uso del comando que usamos actualmente, el npm run webpack, que recordemos es una manera corta (o un alias) del comando ./node_modules/.bin/webpack.cmd -w. Es decir, vamos a fusionar dos tareas en una (ejecutar Webpack y generar un local server).
1. Lo primero que hacemos es cancelar el live-server.
2. Instalamos vía npm el paquete webpack-dev-server.
npm install webpack-dev-server
3. Configuramos el paquete en el archivo webpack.config.js. Para eso necesitamos otra propiedad, al nivel de entry y output. La llamaremos devServer. Esta propiedad es un objeto con dos propiedades dentro:
- contentBase: contiene el directorio o carpeta donde está el archivo JS que queremos ejecutar (en inglés, serve) en el navegador.
- es un absolute path, así que usamos el método path.resolve() para acceder a la raíz de nuestro proyecto.
- publicPath: contiene el path al archivo concreto que se ejecutará en el navegador (el bundle.js). No hace falta escribir el path completo, solo la parte que va desde nuestra carpeta dist hasta el archivo bundle.js.
const path = require('path'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist/assets'), filename: 'bundle.js' }, devServer: { contentBase: path.resolve(__dirname, 'dist'), publicPath: '/assets/' } };
Esta configuración generará unos archivos virtuales, lo que supone que Webpack no ejecutará el archivo bundle.js, sino que hará una copia virtual del mismo, almacenándolo en tu memoria local.
4. Vamos al package.json y escribimos un nuevo script, llamado serve.
"scripts": { "babel": "./node_modules/.bin/babel.cmd ./src/index.js -w -o ./dist/assets/bundle.js", "webpack": "./node_modules/.bin/webpack.cmd -w", "serve": "webpack-dev-server" },
5. Ejecutamos el script en nuestra terminal.
npm run serve
Si todo ha salido bien, la terminal te debería devolver un bloque de código entre el que se encuentra la línea
i 「wds」: Project is running at http://localhost:8080/
que nos indica el puerto local (en inglés, local host) en el que está ejecutándose en tu pc. Haz ctrl+clic para abrirlo. En la consola podrás ver el mismo código que antes. 😊
| Elements Console Sources Performance Network ...
Además, el dev server de Webpack ya está en modo "watching for changes" de manera automática. Pruébalo haciendo algún cambio en el index.js, por ejemplo haciendo un console.log.
console.log('test 1 of the webpack dev server');
Si guardas, verás que en tu consola ya se ven los cambios. Pero hay un pequeño problemilla. Al permitir que Webpack cree archivos virtuales, nuestro código no se está comprimiendo físicamente en ningún archivo, como sí sucedía antes con el bundle.js. 🤨
Puedes comprobarlo borrando todo el contenido del bundle.js. Si ahora escribes cualquier código en en el index.js, por ejemplo:
console.log('test 2 of the webpack dev server');
verás que el cambio se ve reflejado en tu consola, pero no hay absolutamente nada en el bundle.js. Esto es genial para la fase de desarrollo de nuestra app, porque mejora y acelera el rendimiento. Pero supone un problema para la fase de despliegue de nuestra app (en inglés, deployment o production), porque no existe ningún archivo físico que contenga nuestra código comprimido.
Vamos a solucionar eso en la siguiente sección.
Cómo configurar los modos de desarrollo y de producción
Cuando trabajamos en cualquier proyecto, existen dos fases fundamentales en las que podemos encontrarnos. La primera es la fase de desarrollo (en inglés, development mode), que es el momento en el que escribes el código de tu app. 👩💻
🚀 La segunda fase es la de despliegue o producción (en inglés, production mode), que es cuando ya hemos terminado de escribir el código de nuestra app y éste debe estar en un formato optimizado para poder subirlo a un servidor. Esta fase se relaciona con el concepto inglés de "build".
En la sección anterior hemos hecho una configuración muy válida para trabajar en development mode, pero inútil para pasar a la fase de production 😅. Vamos a solucionar eso trabajando con el archivo package.json.
Si nos fijamos en este script:
"serve": "webpack-dev-server"
podemos deducir que éste es el script que nos permite trabajar en development mode. Vamos a crear otro script que nos permita crear un código en production mode listo para subir a un servidor. Es decir, necesitamos que nuestro código vuelva a hacer lo que hacía antes: comprimirse en un sólo archivo final optimizado (el bundle.js).
1. Para eso vamos a usar el script de webpack que ya tenemos, pero eliminándole el banderín de -w, porque en production mode no vamos a necesitar que nuestro código esté "watching for changes". Entre otras cosas, porque para eso está el script para el development mode ("serve").
2. Le cambiamos el nombre al script "webpack" por "build". Y con esto ya tendríamos:
- un script para dev mode 👉 "serve"
- un script para prod mode 👉 "build"
3. Borramos el script de Babel porque posteriormente lo incorporaremos en el dev mode.
"scripts": { "build": "./node_modules/.bin/webpack.cmd", "serve": "webpack-dev-server" },
Si ahora hacemos desde la terminal npm run build, deberías ver que tu archivo bundle.js vuelve a estar lleno de código optimizado. ¡Chachi! 😊
👀 Puede que te hayas fijado en el mensaje de aviso que nos sale cada vez que ejecutamos un comando de Webpack:
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
Básicamente nos está indicando que deberíamos establecer un modo a nuestros scripts, porque lo que hemos hecho hasta ahora es sólo una primera parte de la configuración. Webpack nos pide eso porque así tiene una forma clara de identificar cuándo estamos trabajando en development mode y cuándo en production.
Webpack distingue entre estos dos modos porque si estamos en development mode, Webpack funcionará de manera muy rápida y fluida, pero los archivos finales no estarán optimizados para producción.
En cambio, en el modo de producción, Webpack se ejecutará más lento, porque se centra en optimizar los archivos y comprimirlos para que luego se ejecuten más eficientemente cuando los subamos a un servidor.
Para definir esos modos que nos pide Webpack, no tenemos más que especificarlos en los scripts:
"scripts": { "build": "./node_modules/.bin/webpack.cmd --mode production", "serve": "webpack-dev-server --mode development" },
Y con estos cambios, si ahora haces npm run build, el mensaje de aviso debería haber desaparecido. 👌
Cómo integrar Babel con Webpack
Tal y como hemos mencionado antes, al hacer toda la configuración para poder utilizar Webpack, hemos dejado a Babel atrás 😕. Puedes comprobarlo si vas a tu bundle.js. Verás por ahí algunas ES6 features, como las arrow functions.
Vamos a introducir Babel en nuestra configuración. Para eso, trabajaremos con un concepto nuevo, los "loaders", en el archivo webpack.config.js.
Los loaders son tareas que podemos ejecutar en nuestro código cuando se carga a través del sistema de módulos, que es el sistema que actualmente estamos utilizando (usando imports y exports). Aplicar Babel será ser una de esas tareas que se ejecuten cuando nuestro código se cargue. Por tanto, lo que vamos a hacer es aplicar un Babel loader.
1. Pero primero debemos instalar vía npm el Babel loader y guardarlo como dev dependencies.
npm install babel-loader --save-dev
2. Creamos una propiedad en el objeto module.exports, llamada "module", al nivel de entry, output y devServer. Aquí es donde especificamos qué archivos debe procesar Babel y convertir en código compatible. Dentro de esta propiedad añadiremos:
- propiedad rules: un array de objetos, donde cada objeto es una regla.
- Aquí dentro haremos una comprobación de ciertos archivos para ver si pasan el filtro que establezcamos. Necesitamos crear un filtro porque no queremos aplicar Babel a cualquier archivo, sino solo a ciertos archivos JS. Concretamente, a los archivos JS de la carpeta src.
- Aquí dentro haremos una comprobación de ciertos archivos para ver si pasan el filtro que establezcamos. Necesitamos crear un filtro porque no queremos aplicar Babel a cualquier archivo, sino solo a ciertos archivos JS. Concretamente, a los archivos JS de la carpeta src.
Así que el primer paso será crear una propiedad que se llamará test, y consistirá en una Regular Expression que establecerá el filtro para detectar archivos JS. Lo haremos detectando cualquier archivo que acabe en ".js". 🧐 Ojo al punto porque es importante. Para que la RegEx no lo interprete como "cualquier caracter", utilizamos "\" para evitar ese comportamiento (en inglés, a esto se le llama "escape the character").
Nuestro código así quedaría incompleto, porque hemos dicho que solo queremos procesar los archivos JS de dentro de la carpeta src, pero la RegEx también buscará y encontrará cualquier archivo JS que se encuentre en los node_modules. Y no queremos eso.
- Para arreglar esto, especificamos una propiedad de exclusión (en inglés, exclude). Donde le decimos, a través de otra RegEx, qué queremos excluir de la búsqueda y filtro que haga la RegEx de la propiedad test. Los node_modules son los que queremos excluir.
- Establecemos qué loader usar, mediante una propiedad llamada use. Esta propiedad es un objeto compuesto por dos propiedades:
- el loader 👉 donde le indicamos que queremos usar babel-loader.
- options 👉 un objeto con una propiedad:
- presets 👉 le indicamos en un arrray qué preset queremos usar para hacer la conversión de nuestro código, que será @babel/preset-env, igual que le habíamos indicado en el archivo .babelrc.
const path = require('path'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist/assets'), filename: 'bundle.js' }, devServer: { contentBase: path.resolve(__dirname, 'dist'), publicPath: '/assets/' }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }] } };
3. Probamos nuestro código ejecutando en la terminal npm run build. Si todo ha salido bien, deberías ver tu código comprimido en el bundle.js, con características antiguas de JS como var en lugar de let o const. 👌
4. Borramos el archivo .babelrc porque ya no lo vamos a necesitar.
Consideraciones finales
A lo largo de esta serie de posts hemos aprendido a configurar Babel y Webpack, dos herramientas muy potentes para cualquier frontend developer 😎. El proceso puede parecer tedioso, pero piensa que sólo tendrás que hacerlo una vez. Es más, con el código de esta serie podrás hacerte tu propia plantilla (en inglés, boilerplate), y aplicarla a cualquiera de tus proyectos.
En cualquier caso, uno de mis profes, The Net Ninja, tiene un boilerplate listo para usar aquí. Si decides usarlo, los pasos a seguir serían:
1. Descargar el archivo ZIP del boilerplate desde GitHub pulsando el botón
2. Extrae los archivos en la carpeta que quieras y créate una nueva carpeta para tu proyecto si no la tienes ya, que puede llamarse por ejemplo "proyecto-test-1".
3. Copia todos los archivos de la carpeta boilerplate dentro de tu proyecto-test-1. Si ahora abres la carpeta de tu proyecto en vsCode, ya tienes tu boilerplate listo para usar.
4. Para poder empezar, debemos instalar los node_modules con el comando npm install.
5. Por último, antes de empezar a escribir código, ejecutamos el webpack dev server con el comando npm run serve, que recordemos, nos pone en dev mode, y nos proporciona un local server donde podremos ver nuestra app.
Cuando estemos listos para desplegar nuestra app y subirla a un servidor, usaremos npm run build. ¡Y listo!
THE END!
¡Y con esto terminamos nuestra saga sobre Babel y Webpack! Espero que hayas aprendido algo nuevo 😊. Si te queda alguna duda, ¡nos vemos en los comentarios! Si quieres seguir aprendiendo, aquí tienes un proyecto construido usando Webpack.
Y si crees que este post puede serle útil a alguien, ¡compártelo!
Otros artículos que pueden interesarte