Configuración de un proyecto ES6 usando Babel y webpack
En este artículo, vamos a ver la creación de una configuración de construcción para el manejo de JavaScript moderno (que se ejecuta en los navegadores web) utilizando Babel y webpack.
Esto es necesario para asegurar que nuestro código JavaScript moderno, en particular, se hace compatible con una gama más amplia de navegadores de lo que podría ser de otra manera.
JavaScript, como la mayoría de las tecnologías relacionadas con la web, está evolucionando todo el tiempo. En los viejos tiempos, podíamos colocar un par de etiquetas <script>
en una página, tal vez incluir jQuery y un par de plugins, y ya estaba todo listo.
Sin embargo, desde la introducción de ES6, las cosas se han complicado progresivamente. El soporte de los navegadores para las nuevas características del lenguaje es a menudo irregular, y a medida que las aplicaciones de JavaScript se vuelven más ambiciosas, los desarrolladores están empezando a utilizar módulos para organizar su código. A su vez, esto significa que si estás escribiendo un JavaScript moderno hoy en día, tendrás que introducir un paso de compilación en tu proceso.
Como puedes ver en los enlaces de abajo, la conversión de ES6 a ES5 aumenta drásticamente el número de navegadores que podemos soportar.
- Compatibilidad con ES6
- Compatibilidad con ES5
El propósito de un sistema de compilación es automatizar el flujo de trabajo necesario para tener nuestro código listo para los navegadores y la producción. Esto puede incluir pasos como transpilar el código a un estándar diferente, compilar Sass a CSS, agrupar archivos, minificar y comprimir el código, y muchos otros. Para asegurar que estos son consistentemente repetibles, se necesita un sistema de construcción para iniciar los pasos en una secuencia conocida desde un solo comando.
Requisitos previos
Para seguir adelante, necesitarás tener tanto Node.js como npm instalados (vienen empaquetados juntos). Yo recomendaría el uso de un gestor de versiones como nvm para gestionar su instalación de Node (aquí es cómo), y si usted quiere un poco de ayuda para llegar a los apretones con npm, a continuación, echa un vistazo a SitePoint’s principiante amigable tutorial npm.
Set Up
Crear una carpeta raíz en algún lugar de su equipo y navegar en él desde su terminal / línea de comandos. Esta será su carpeta <ROOT>
.
Cree un archivo package.json
con esto:
npm init -y
Nota: La bandera -y
crea el archivo con la configuración predeterminada, y significa que no necesita completar ninguno de los detalles habituales desde la línea de comandos. Se pueden cambiar en su editor de código más tarde si lo desea.
Dentro de su carpeta <ROOT>
, haga los directorios src
, src/js
, y public
. La carpeta src/js
será donde pondremos nuestro código fuente sin procesar, y la carpeta public
será donde acabará el código transpilado.
Transpilando con Babel
Para ponernos en marcha, vamos a instalar babel-cli, que proporciona la capacidad de transpilar ES6 a ES5, y babel-preset-env, que nos permite apuntar a versiones específicas del navegador con el código transpilado.
npm install babel-cli babel-preset-env --save-dev
Ahora deberías ver lo siguiente en tu package.json
:
"devDependencies": { "babel-cli": "^6.26.0", "babel-preset-env": "^1.6.1"}
Mientras estamos en el archivo package.json
, cambiemos la sección scripts
para que se lea así:
"scripts": { "build": "babel src -d public"},
Esto nos da la posibilidad de llamar a Babel a través de un script, en lugar de hacerlo directamente desde la terminal cada vez. Si quieres saber más sobre los scripts npm y lo que pueden hacer, echa un vistazo a este tutorial de SitePoint.
Por último, antes de que podamos probar si Babel está haciendo lo suyo, necesitamos crear un archivo de configuración .babelrc
. Esto es lo que nuestro paquete babel-preset-env
hará referencia a sus parámetros de transpilación.
Cree un nuevo archivo en su directorio <ROOT>
llamado .babelrc
y pegue lo siguiente en él:
{ "presets": } } ] ]}
Esto configurará Babel para transpilar para las dos últimas versiones de cada navegador, además de Safari en v7 o superior. Hay otras opciones disponibles dependiendo de los navegadores que necesites soportar.
Con esto guardado, ahora podemos probar las cosas con un archivo JavaScript de ejemplo que utiliza ES6. Para los propósitos de este artículo, he modificado una copia de leftpad para utilizar la sintaxis ES6 en una serie de lugares: literales de plantilla, funciones de flecha, const y let.
Guarda esto como src/js/leftpad.js
y desde tu terminal ejecuta lo siguiente:
npm run build
Si todo es como se pretende, en tu carpeta public
deberías encontrar ahora un nuevo archivo llamado js/leftpad.js
. Si lo abre, verá que ya no contiene ninguna sintaxis ES6 y que tiene el siguiente aspecto:
Organizar su código con módulos ES6
Un módulo ES6 es un archivo JavaScript que contiene funciones, objetos o valores primitivos que desea poner a disposición de otro archivo JavaScript. Usted export
de uno, y import
en el otro. Cualquier proyecto moderno de JavaScript debería considerar el uso de módulos. Le permiten dividir su código en unidades autocontenidas y por lo tanto hacer las cosas más fáciles de mantener; le ayudan a evitar la contaminación del espacio de nombres; y ayudan a hacer su código más portátil y reutilizable.
Mientras que la mayoría de la sintaxis de ES6 está ampliamente disponible en los navegadores modernos, este no es todavía el caso de los módulos. En el momento de escribir este artículo, están disponibles en Chrome, Safari (incluida la última versión para iOS) y Edge; están ocultos tras una bandera en Firefox y Opera; y no están disponibles (y probablemente nunca lo estarán) en IE11, ni en la mayoría de los dispositivos móviles.
En la siguiente sección, veremos cómo podemos integrar los módulos en nuestra configuración de compilación.
Exportar
La palabra clave exportar es lo que nos permite hacer que nuestros módulos ES6 estén disponibles para otros archivos, y nos da dos opciones para hacerlo: con nombre y por defecto. Con la exportación con nombre, puedes tener múltiples exportaciones por módulo, y con una exportación por defecto sólo tienes una por módulo. Las exportaciones con nombre son particularmente útiles cuando se necesita exportar varios valores. Por ejemplo, usted puede tener un módulo que contiene una serie de funciones de utilidad que deben estar disponibles en varios lugares dentro de sus aplicaciones.
Así que vamos a convertir nuestro archivo leftPad
en un módulo, que luego podemos requerir en un segundo archivo.
Exportación con nombre
Para crear una exportación con nombre, añade lo siguiente al final del archivo leftPad
:
export { leftPad };
También podemos eliminar la declaración "use strict";
de la parte superior del archivo, ya que los módulos se ejecutan en modo estricto por defecto.
Exportación de fallos
Como sólo hay una función a exportar en el fichero leftPad
, podría ser un buen candidato para usar export default
en su lugar:
export default function leftPad(str, len, ch) { ...}
De nuevo, puedes eliminar la declaración "use strict";
de la parte superior del fichero.
Importar
Para hacer uso de los módulos exportados, ahora tenemos que importarlos en el archivo (módulo) en el que deseamos utilizarlos.
Para la opción export default
, el módulo exportado puede importarse con el nombre que se desee. Por ejemplo, el módulo leftPad
puede importarse así:
import leftPad from './leftpad';
O puede importarse con otro nombre, así:
import pineapple_fritter from './leftpad';
Funcionalmente, ambos funcionarán exactamente igual, pero obviamente tiene sentido utilizar el mismo nombre con el que se exportó, o algo que haga comprensible la importación – tal vez cuando el nombre exportado choca con otro nombre de variable que ya existe en el módulo receptor.
Para la opción de exportación con nombre, debemos importar el módulo usando el mismo nombre con el que fue exportado. Para nuestro módulo de ejemplo, lo importaríamos de una manera similar a la que utilizamos con la sintaxis export default
, pero en este caso, debemos envolver el nombre importado con llaves:
import { leftPad } from './leftpad';
Las llaves son obligatorias con una exportación con nombre, y fallará si no se utilizan.
Es posible cambiar el nombre de una exportación con nombre en la importación si es necesario, y para ello, tenemos que modificar nuestra sintaxis un poco usando una sintaxis import as
. Al igual que con export
, hay una variedad de maneras de hacer esto, todos los cuales se detallan en la página de importación MDN.
import { leftPad as pineapple_fritter } from './leftpad_es6';
De nuevo, el cambio de nombre es un poco sin sentido, pero ilustra el punto de que se pueden cambiar a cualquier cosa. Usted debe mantener las buenas prácticas de nomenclatura en todo momento, a menos que, por supuesto, usted está escribiendo rutinas para la preparación de recetas a base de frutas.
Consumiendo el módulo exportado
Para hacer uso del módulo exportado leftPad
, he creado el siguiente archivo index.js
en la carpeta src/js
. Aquí, hago un bucle a través de una matriz de números de serie, y los prefijo con ceros para convertirlos en una cadena de ocho caracteres. Más adelante, haremos uso de esto y los publicaremos en un elemento de lista ordenada en una página HTML. Tenga en cuenta que este ejemplo utiliza la sintaxis de exportación por defecto:
Como hicimos antes, ejecute el script de construcción desde el directorio <ROOT>
:
npm run build
Babel creará ahora un archivo index.js
en el directorio public/js
. Al igual que con nuestro archivo leftPad.js
, debería ver que Babel ha reemplazado toda la sintaxis ES6 y ha dejado sólo la sintaxis ES5. También puedes notar que ha convertido la sintaxis del módulo ES6 a la basada en Node module.exports
, lo que significa que podemos ejecutarlo desde la línea de comandos:
node public/js/index.js//
Tu terminal debería ahora registrar una matriz de cadenas prefijadas con ceros para que todas tengan ocho caracteres. Una vez hecho esto, es hora de echar un vistazo a webpack.
Presentación de webpack e integración con Babel
Como se ha mencionado, los módulos ES6 permiten al desarrollador de JavaScript dividir su código en trozos manejables, pero la consecuencia de esto es que esos trozos tienen que ser servidos al navegador solicitante, añadiendo potencialmente docenas de peticiones HTTP adicionales al servidor – algo que realmente deberíamos tratar de evitar. Aquí es donde entra webpack.
webpack es un agrupador de módulos. Su propósito principal es procesar tu aplicación rastreando todas sus dependencias, y luego empaquetarlas todas en uno o más paquetes que pueden ser ejecutados en el navegador. Sin embargo, puede ser mucho más que eso, dependiendo de cómo se configure.
La configuración de webpack se basa en cuatro componentes clave:
- un punto de entrada
- una ubicación de salida
- cargadores
- plugins
Entrada: Esto mantiene el punto de inicio de su aplicación desde donde webpack puede identificar sus dependencias.
Salida: Especifica dónde quieres que se guarde el bundle procesado.
Loaders: Son una forma de convertir una cosa como entrada y generar otra como salida. Se pueden utilizar para ampliar las capacidades de webpack para manejar algo más que archivos JavaScript, y por lo tanto convertirlos también en módulos válidos.
Plugins: Estos se utilizan para ampliar las capacidades de webpack en otras tareas más allá de la agrupación – como la minificación, linting y optimización.
Para instalar webpack, ejecute lo siguiente desde su directorio <ROOT>
:
npm install webpack webpack-cli --save-dev
Esto instala webpack localmente en el proyecto, y también da la capacidad de ejecutar webpack desde la línea de comandos mediante la adición de webpack-cli
. Ahora deberías ver webpack en tu archivo package.json
. Mientras estás en ese archivo, modifica la sección de scripts como sigue, para que ahora sepa usar webpack en lugar de Babel directamente:
"scripts": { "build": "webpack --config webpack.config.js"},
Como puedes ver, este script está llamando a un archivo webpack.config.js
, así que vamos a crearlo en nuestro directorio <ROOT>
con el siguiente contenido:
Este es más o menos el archivo de configuración más simple que necesitas con webpack. Puedes ver que utiliza las secciones de entrada y salida descritas anteriormente (podría funcionar sólo con ellas), pero también contiene un ajuste mode: 'development'
.
webpack tiene la opción de utilizar los modos «desarrollo» o «producción». La configuración mode: 'development'
optimiza la velocidad de compilación y la depuración, mientras que mode: 'production'
optimiza la velocidad de ejecución en tiempo real y el tamaño del archivo de salida. Hay una buena explicación de los modos en el artículo de Tobias Koppers «webpack 4: modo y optimización» si desea leer más sobre cómo se pueden configurar más allá de la configuración predeterminada.
A continuación, elimine cualquier archivo de la carpeta public/js
. Luego vuelva a ejecutar esto:
npm run build
Verá que ahora contiene un solo archivo ./public/bundle.js
. Sin embargo, al abrir el nuevo archivo, los dos archivos con los que empezamos tienen un aspecto bastante diferente. Esta es la sección del archivo que contiene el código index.js
. Aunque está bastante modificado con respecto a nuestro original, todavía puedes distinguir sus nombres de variables:
Si ejecutas node public/js/bundle.js
desde la carpeta <ROOT>
, verás que obtienes los mismos resultados que teníamos anteriormente.
Transpilando
Como hemos mencionado antes, los cargadores nos permiten convertir una cosa en otra. En este caso, queremos que ES6 se convierta en ES5. Para ello, necesitaremos un par de paquetes más:
npm install babel-loader babel-core --save-dev
Para utilizarlos, el webpack.config.js
necesita que se le añada una sección de módulo después de la sección de salida, así:
Esto utiliza una sentencia regex para identificar los archivos JavaScript que se van a transpilar con el babel-loader
, mientras que excluye cualquier cosa de la carpeta node_modules
de eso. Por último, se le dice al babel-loader
que utilice el paquete babel-preset-env
instalado anteriormente, para establecer los parámetros de transpilación establecidos en el archivo .babelrc
.
Una vez hecho esto, puedes volver a ejecutar esto:
npm run build
Entonces comprueba el nuevo public/js/bundle.js
y verás que todo rastro de sintaxis ES6 ha desaparecido, pero sigue produciendo la misma salida que antes.
Llevándolo al navegador
Habiendo construido una configuración de webpack y Babel que funciona, es hora de llevar lo que hemos hecho al navegador. Un pequeño archivo HTML es necesario, y esto debe ser creado en la carpeta <ROOT>
como a continuación:
Un poco más de JavaScript es necesario para mostrar la lista, así que vamos a alterar ./src/js/index.js
para que esto suceda:
Ahora, si abres index.html
en tu navegador, deberías ver que aparece una lista ordenada, así:
Continuando con la configuración anterior, nuestro sistema de compilación está prácticamente listo para funcionar. Ahora podemos usar webpack para empaquetar nuestros módulos y transpilar el código ES6 a ES5 con Babel.
Sin embargo, es un poco molesto que, para transpilar nuestro código ES6, tengamos que ejecutar npm run build
cada vez que hagamos un cambio.
Añadir un ‘watch’
Para superar la necesidad de ejecutar repetidamente npm run build
, puedes configurar un 'watch'
en tus archivos y hacer que webpack recompile automáticamente cada vez que vea un cambio en uno de los archivos de la carpeta ./src
. Para implementarlo, modifica la sección scripts
del archivo package.json
, como se indica a continuación:
"scripts": { "watch": "webpack --watch", "build": "webpack --config webpack.config.js"},
Para comprobar que funciona, ejecuta npm run watch
desde el terminal, y verás que ya no vuelve al símbolo del sistema. Ahora vuelve a src/js/index.js
y añade un valor extra en el array serNos
y guárdalo. El mío ahora se ve así:
const serNos = ;
Si ahora compruebas el terminal, verás que ha cerrado la sesión, y que ha vuelto a ejecutar la tarea webpack build
. Y al volver al navegador y refrescar, verás el nuevo valor añadido al final de la lista, al haber sido procesado con leftPad
.
Refrescar el navegador automáticamente
Ahora estaría muy bien si pudiéramos conseguir que webpack refrescara el navegador automáticamente cada vez que hiciéramos un cambio. Vamos a hacerlo instalando un paquete npm adicional llamado webpack-dev-server
. Sin embargo, ¡no te olvides de hacer Ctrl + c en la tarea watch
!
npm install webpack-dev-server --save-dev
Una vez hecho esto, vamos a añadir un nuevo script al archivo package.json
para llamar al nuevo paquete. La sección scripts
debe contener ahora esto:
Nota la bandera --open-page
añadida al final del script. Esto le dice a webpack-dev-server
que abra una página específica en su navegador por defecto utilizando su modo iframe.
Ahora ejecute npm start
y debería ver que se abre una nueva pestaña del navegador en con la lista de piezas que se muestra. Para demostrar que el
'watch'
está funcionando, vaya a src/js/index.js
y añada otro valor nuevo al final de la matriz serNos
. Cuando guardes los cambios, deberías notarlos reflejados casi inmediatamente en el navegador.
Con esto completado, lo único que queda es que el modo en webpack.config.js
se establezca en production
. Una vez establecido esto, webpack también minificará el código que emita en ./public/js/bundle.js
. Debes tener en cuenta que si el mode
no se establece, webpack utilizará por defecto la configuración production
.
Conclusión
En este artículo, has visto cómo configurar un sistema de construcción para JavaScript moderno. Inicialmente, esto utilizó Babel desde la línea de comandos para convertir la sintaxis ES6 a ES5. Luego has visto cómo hacer uso de los módulos ES6 con las palabras clave export
y import
, cómo integrar webpack para realizar una tarea de empaquetado, y cómo añadir una tarea de vigilancia para automatizar la ejecución de webpack cada vez que se detectan cambios en un archivo fuente. Finalmente has visto cómo instalar webpack-dev-server
para refrescar la página automáticamente cada vez que se realiza un cambio.
Si deseas llevar esto más allá, te sugiero que leas la inmersión profunda de SitePoint en webpack y la agrupación de módulos, así como la investigación de cargadores y plugins adicionales que permitirán a webpack manejar las tareas de Sass y la compresión de activos. También mira el eslint-loader
y el plugin para Prettier también.
Happy bundling …