Logotipo de Zephyrnet

Introducción a esbuild Bundler — SitePoint

Fecha:

esconstruir es un paquete rápido que puede optimizar código JavaScript, TypeScript, JSX y CSS. Este artículo lo ayudará a ponerse al día con esbuild y le mostrará cómo crear su propio sistema de compilación sin otras dependencias.

Índice del contenido
  1. ¿Cómo funciona esbuild?
  2. ¿Por qué paquete?
  3. ¿Por qué usar esbuild?
  4. ¿Por qué evitar esbuild?
  5. Inicio súper rápido
  6. Proyecto de ejemplo
  7. Resumen del proyecto
  8. Configuración de esbuild
  9. Paquete de JavaScript
  10. Paquete de CSS
  11. Velar, reconstruir y servir
  12. Resumen

¿Cómo funciona esbuild?

Marcos como tornillo ha adoptado esbuild, pero puede usar esbuild como una herramienta independiente en sus propios proyectos.

  • esbuild empaqueta código JavaScript en un solo archivo de forma similar a los paquetes como Enrollar. Esta es la función principal de esbuild, y resuelve módulos, informa problemas de sintaxis, "sacudidas de árboles" para eliminar funciones no utilizadas, borra declaraciones de registro y depuración, minimiza el código y proporciona mapas de origen.

  • esbuild empaqueta el código CSS en un solo archivo. No es un sustituto completo de los preprocesadores como Hablar con descaro a or PostCSS, pero esbuild puede manejar parciales, problemas de sintaxis, anidamiento, codificación de activos en línea, mapas de origen, prefijo automático y minificación. Eso puede ser todo lo que necesitas.

  • esbuild también proporciona un servidor de desarrollo local con agrupación automática y recarga en caliente, por lo que no es necesario actualizar. No tiene todas las características que ofrece sincronización del navegador, pero es lo suficientemente bueno para la mayoría de los casos.

El siguiente código lo ayudará a comprender los conceptos de esbuild para que pueda investigar más oportunidades de configuración para sus proyectos.

¿Por qué paquete?

La agrupación de código en un solo archivo ofrece varios beneficios. Éstos son algunos de ellos:

  • puede desarrollar archivos de origen más pequeños e independientes que son más fáciles de mantener
  • puede aplicar pelusa, embellecer y verificar la sintaxis del código durante el proceso de agrupación
  • el empaquetador puede eliminar funciones no utilizadas, conocidas como temblor de árboles
  • puede agrupar versiones alternativas del mismo código y crear objetivos para navegadores más antiguos, Node.js, Deno, etc.
  • los archivos individuales se cargan más rápido que varios archivos y el navegador no requiere compatibilidad con el módulo ES
  • la agrupación a nivel de producción puede mejorar el rendimiento al minimizar el código y eliminar las declaraciones de registro y depuración

¿Por qué usar esbuild?

A diferencia de los paquetes de JavaScript, esbuild es un ejecutable de Go compilado que implementa un procesamiento paralelo pesado. Es rápido y hasta cien veces más rápido que Rollup, Parcel o Webpack. Podría ahorrar semanas de tiempo de desarrollo durante la vida útil de un proyecto.

Además, esbuild también ofrece:

¿Por qué evitar esbuild?

En el momento de escribir este artículo, esbuild ha alcanzado la versión 0.18. Es confiable pero sigue siendo un producto beta.

esbuild se actualiza con frecuencia y las opciones pueden cambiar entre versiones. El documentación recomienda que te quedes con una versión específica. Puede actualizarlo, pero es posible que deba migrar sus archivos de configuración y profundizar en la nueva documentación para descubrir cambios importantes.

Tenga en cuenta también que esbuild no realiza la verificación de tipos de TypeScript, por lo que aún deberá ejecutar tsc -noEmit.

Inicio súper rápido

Si es necesario, cree un nuevo proyecto Node.js con npm init, luego instale esbuild localmente como una dependencia de desarrollo:

npm install esbuild --save-dev --save-exact

La instalación requiere alrededor de 9 MB. Verifique que funcione ejecutando este comando para ver la versión instalada:

./node_modules/.bin/esbuild --version

O ejecute este comando para ver la ayuda de la CLI:

./node_modules/.bin/esbuild --help

Utilice la API de la CLI para agrupar un script de entrada (myapp.js) y todos sus módulos importados en un solo archivo llamado bundle.js. esbuild generará un archivo utilizando el formato de expresión de función de invocación inmediata (IIFE) predeterminado, orientado al navegador:

./node_modules/.bin/esbuild myapp.js --bundle --outfile=bundle.js

solicite instalar esbuild de otras formas si no está utilizando Node.js.

Proyecto de ejemplo

Descargue los archivos de ejemplo y un configuración de esbuild desde Github. Es un proyecto de Node.js, así que instale la dependencia única de esbuild con:

npm install

Cree los archivos fuente en src a una build directorio e iniciar un servidor de desarrollo con:

npm start

Ahora navega a localhost:8000 en su navegador para ver una página web que muestra un reloj en tiempo real. Cuando actualiza cualquier archivo CSS en src/css/ or src/css/partials, esbuild volverá a empaquetar el código y recargará los estilos en vivo.

proyecto de reloj de ejemplo esbuild

Prensa Ctrl|Cmd + Ctrl|Cmd para detener el servidor

Cree una compilación de producción para la implementación usando:

npm run build

Examine los archivos CSS y JavaScript en el build directorio para ver las versiones minificadas sin mapas fuente.

Resumen del proyecto

La página del reloj en tiempo real está construida en un build directorio usando archivos fuente de src.

El package.json archivo define cinco npm guiones. El primero elimina el build directorio:

"clean": "rm -rf ./build",

Antes de que ocurra cualquier agrupación, un init la secuencia de comandos se ejecuta clean, crea un nuevo build directorio y copias:

  1. un archivo HTML estático de src/html/index.html a build/index.html
  2. imágenes estáticas de src/images/ a build/images/
"init": "npm run clean && mkdir ./build && cp ./src/html/* ./build/ && cp -r ./src/images ./build",

An esbuild.config.js El archivo controla el proceso de agrupación de esbuild mediante la API de JavaScript. Esto es más fácil de administrar que pasar opciones a la API de la CLI, lo que puede volverse difícil de manejar. Un npm bundle la secuencia de comandos se ejecuta init seguido por node ./esbuild.config.js:

"bundle": "npm run init && node ./esbuild.config.js",

Los últimos dos npm ejecutar scripts bundle ya sea con un production or development parámetro pasado a ./esbuild.config.js para controlar la construcción:

"build": "npm run bundle -- production",
"start": "npm run bundle -- development"

Cuándo ./esbuild.config.js se ejecuta, determina si debe crear minified production archivos (predeterminado) o development archivos con actualizaciones automáticas, mapas de origen y un servidor de recarga en vivo. En ambos casos, paquetes de esbuild:

  • el archivo CSS de entrada src/css/main.css a build/css/main.css
  • el archivo JavaScript de entrada scr/js/main.js a build/js/main.js

Configuración de esbuild

package.json tiene un "type" of "module" así que todo .js Los archivos pueden usar módulos ES. El esbuild.config.js importaciones de guiones esbuild y conjuntos productionMode a true al agrupar para la producción o false al agrupar para el desarrollo:

import { argv } from 'node:process';
import * as esbuild from 'esbuild'; const productionMode = ('development' !== (argv[2] || process.env.NODE_ENV)), target = 'chrome100,firefox100,safari15'.split(','); console.log(`${ productionMode ? 'production' : 'development' } build`);

Paquete objetivo

Tenga en cuenta que variable objetivo define una matriz de navegadores y números de versión para usar en la configuración. Esto afecta la salida del paquete y cambia la sintaxis para admitir plataformas específicas. Por ejemplo, esbuild puede:

  • expandir anidamiento de CSS nativo en selectores completos (el anidamiento permanecería si "Chrome115" era el único objetivo)
  • agregue propiedades con el prefijo del proveedor de CSS cuando sea necesario
  • polillenar el ?? operador coalescente nulo
  • remove # de campos de clase privada

Además de los navegadores, también puede orientar node y es versiones como es2020 y esnext (las últimas características de JS y CSS).

Paquete de JavaScript

La API más simple para crear un paquete:

await esbuild.build({ entryPoints: ['myapp.js'], bundle: true outfile: 'bundle.js'
});

Esto replica el comando CLI utilizado anteriormente:

./node_modules/.bin/esbuild myapp.js --bundle --outfile=bundle.js

El proyecto de ejemplo utiliza opciones más avanzadas, como la visualización de archivos. Esto requiere una compilación de larga duración. contexto que establece la configuración:


const buildJS = await esbuild.context({ entryPoints: [ './src/js/main.js' ], format: 'esm', bundle: true, target, drop: productionMode ? ['debugger', 'console'] : [], logLevel: productionMode ? 'error' : 'info', minify: productionMode, sourcemap: !productionMode && 'linked', outdir: './build/js' });

ofertas esbuild decenas de opciones de configuración. Aquí hay un resumen de los que se usan aquí:

  • entryPoints define una matriz de puntos de entrada de archivos para la agrupación. El proyecto de ejemplo tiene un script en ./src/js/main.js.

  • format establece el formato de salida. El ejemplo usa esm, pero puede configurar opcionalmente iife para navegadores más antiguos o commonjs para Node.js.

  • bundle establecido en true módulos importados en línea en el archivo de salida.

  • target es la matriz de navegadores de destino definida anteriormente.

  • drop es una serie de console y/o debugger declaraciones a eliminar. En este caso, las compilaciones de producción eliminan ambas y las compilaciones de desarrollo las retienen.

  • logLevel define la verbosidad del registro. El ejemplo anterior muestra errores durante las compilaciones de producción y mensajes de información más detallados durante las compilaciones de desarrollo.

  • minify reduce el tamaño del código eliminando comentarios y espacios en blanco y renombrando variables y funciones donde sea posible. El proyecto de ejemplo se minimiza durante las compilaciones de producción, pero embellece código durante las compilaciones de desarrollo.

  • sourcemap establecido en linked (solo en modo de desarrollo) genera un mapa fuente vinculado en un .map file para que el archivo fuente original y la línea estén disponibles en las herramientas de desarrollo del navegador. También puede configurar inline para incluir el mapa de origen dentro del archivo incluido, both para crear ambos, o external generar un .map archivo sin un enlace del JavaScript incluido.

  • outdir define el directorio de salida del archivo empaquetado.

Llame al objeto de contexto rebuild() método para ejecutar la compilación una vez, normalmente para una compilación de producción:

await buildJS.rebuild();
buildJS.dispose(); 

Llame al objeto de contexto watch() método para seguir ejecutándose y reconstruir automáticamente cuando cambian los archivos observados:

await buildJS.watch();

El objeto de contexto garantiza que las compilaciones posteriores se procesen de forma incremental y que reutilicen el trabajo de compilaciones anteriores para mejorar el rendimiento.

Archivos de entrada y salida JavaScript

La entrada src/js/main.js importaciones de archivos dom.js y time.js módulos de la lib subcarpeta. Encuentra todos los elementos con una clase de clock y establece su contenido de texto a la hora actual cada segundo:

import * as dom from './lib/dom.js';
import { formatHMS } from './lib/time.js'; const clock = dom.getAll('.clock'); if (clock.length) { console.log('initializing clock'); setInterval(() => { clock.forEach(c => c.textContent = formatHMS()); }, 1000); }

dom.js exporta dos funciones. main.js importa ambos pero solo usa getAll():

 export function get(selector, doc = document) { return doc.querySelector(selector);
} export function getAll(selector, doc = document) { return Array.from(doc.querySelectorAll(selector));
}

time.js exporta dos funciones. main.js importaciones formatHMS(), pero que usa las otras funciones en el módulo:

 function timePad(n) { return String(n).padStart(2, '0');
} export function formatHM(d = new Date()) { return timePad(d.getHours()) + ':' + timePad(d.getMinutes());
} export function formatHMS(d = new Date()) { return formatHM(d) + ':' + timePad(d.getSeconds());
}

El paquete de desarrollo resultante elimina (sacudidas de árboles) get() en dom.js pero incluye todos los time.js funciones También se genera un mapa fuente:


function getAll(selector, doc = document) { return Array.from(doc.querySelectorAll(selector));
} function timePad(n) { return String(n).padStart(2, "0");
} function formatHM(d = new Date()) { return timePad(d.getHours()) + ":" + timePad(d.getMinutes());
} function formatHMS(d = new Date()) { return formatHM(d) + ":" + timePad(d.getSeconds());
} var clock = getAll(".clock");
if (clock.length) { console.log("initializing clock"); setInterval(() => { clock.forEach((c) => c.textContent = formatHMS()); }, 1e3);
} 

(Tenga en cuenta que esbuild puede volver a escribir let y const a var para la corrección y la velocidad.)

El paquete de producción resultante reduce el código a 322 caracteres:

function o(t,c=document){return Array.from(c.querySelectorAll(t))}function e(t){return String(t).padStart(2,"0")}function l(t=new Date){return e(t.getHours())+":"+e(t.getMinutes())}function r(t=new Date){return l(t)+":"+e(t.getSeconds())}var n=o(".clock");n.length&&setInterval(()=>{n.forEach(t=>t.textContent=r())},1e3);

Paquete de CSS

La agrupación de CSS en el proyecto de ejemplo utiliza un objeto de contexto similar al de JavaScript anterior:


const buildCSS = await esbuild.context({ entryPoints: [ './src/css/main.css' ], bundle: true, target, external: ['/images/*'], loader: { '.png': 'file', '.jpg': 'file', '.svg': 'dataurl' }, logLevel: productionMode ? 'error' : 'info', minify: productionMode, sourcemap: !productionMode && 'linked', outdir: './build/css' });

Define un external como una matriz de archivos y rutas a excluir de la construcción. En el proyecto de ejemplo, los archivos en el src/images/ directorio se copian en el build directorio para que HTML, CSS o JavaScript puedan hacer referencia a ellos directamente. Si esto no estuviera configurado, esbuild copiaría los archivos a la salida. build/css/ directorio al usarlos en background-image o propiedades similares.

El loader La opción cambia la forma en que esbuild maneja un archivo importado al que no se hace referencia como external activo. En este ejemplo:

  • Las imágenes SVG se integran como URI de datos
  • Las imágenes PNG y JPG se copian en el build/css/ directorio y referenciado como archivos

Archivos de entrada y salida CSS

La entrada src/css/main.css importaciones de archivos variables.css y elements.css del desplegable partials subcarpeta:


@import './partials/variables.css';
@import './partials/elements.css';

variables.css define las propiedades personalizadas predeterminadas:


:root { --font-body: sans-serif; --color-fore: #fff; --color-back: #112;
}

elements.css define todos los estilos. Nota:

  • las body tiene una imagen de fondo cargada desde el externo images directorio
  • las h1 está anidado en el interior header
  • las h1 tiene un SVG de fondo que se insertará
  • los navegadores de destino no requieren prefijos de proveedores

*, *::before, ::after { box-sizing: border-box; font-weight: normal; padding: 0; margin: 0;
} body { font-family: var(--font-body); color: var(--color-fore); background: var(--color-back) url(/images/web.png) repeat; margin: 1em;
} header { & h1 { font-size: 2em; padding-left: 1.5em; margin: 0.5em 0; background: url(../../icons/clock.svg) no-repeat; } } .clock { display: block; font-size: 5em; text-align: center; font-variant-numeric: tabular-nums;
}

El paquete de desarrollo resultante expande la sintaxis anidada, inserta el SVG y genera un mapa fuente:


:root { --font-body: sans-serif; --color-fore: #fff; --color-back: #112;
} *,
*::before,
::after { box-sizing: border-box; font-weight: normal; padding: 0; margin: 0;
}
body { font-family: var(--font-body); color: var(--color-fore); background: var(--color-back) url(/images/web.png) repeat; margin: 1em;
}
header h1 { font-size: 2em; padding-left: 1.5em; margin: 0.5em 0; background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>*{fill:none;stroke:%23fff;stroke-width:1.5;stroke-miterlimit:10}</style></defs><circle cx="12" cy="12" r="10.5"></circle><circle cx="12" cy="12" r="0.95"></circle><polyline points="12 4.36 12 12 16.77 16.77"></polyline></svg>') no-repeat;
}
.clock { display: block; font-size: 5em; text-align: center; font-variant-numeric: tabular-nums;
} 

El paquete de producción resultante minimiza el código a 764 caracteres (aquí se omite el SVG):

:root{--font-body: sans-serif;--color-fore: #fff;--color-back: #112}*,*:before,:after{box-sizing:border-box;font-weight:400;padding:0;margin:0}body{font-family:var(--font-body);color:var(--color-fore);background:var(--color-back) url(/images/web.png) repeat;margin:1em}header h1{font-size:2em;padding-left:1.5em;margin:.5em 0;background:url('data:image/svg+xml,<svg...></svg>') no-repeat}.clock{display:block;font-size:5em;text-align:center;font-variant-numeric:tabular-nums}

Velar, reconstruir y servir

El resto del esbuild.config.js paquetes de secuencias de comandos una vez para compilaciones de producción antes de finalizar:

if (productionMode) { await buildCSS.rebuild(); buildCSS.dispose(); await buildJS.rebuild(); buildJS.dispose(); }

Durante las compilaciones de desarrollo, el script sigue ejecutándose, observa los cambios en los archivos y se empaqueta automáticamente de nuevo. El buildCSS context lanza un servidor web de desarrollo con build/ como directorio raíz:

else { await buildCSS.watch(); await buildJS.watch(); await buildCSS.serve({ servedir: './build' }); }

Inicie la compilación de desarrollo con:

npm start

Luego navega hacia localhost:8000 para ver la página

A diferencia de Browsersync, deberá agregar su propio código a las páginas de desarrollo para recargar en vivo. Cuando se producen cambios, esbuild envía información sobre la actualización a través de un evento enviado por el servidor. La opción más sencilla es recargar completamente la página cuando se produzca algún cambio:

new EventSource('/esbuild').addEventListener('change', () => location.reload());

El proyecto de ejemplo utiliza el objeto de contexto CSS para crear el servidor. Eso es porque prefiero actualizar manualmente los cambios de JavaScript: ¡y porque no pude encontrar una manera para que esbuild enviara un evento para las actualizaciones de CSS y JS! La página HTML incluye la siguiente secuencia de comandos para reemplazar los archivos CSS actualizados sin una actualización completa de la página (recarga en caliente):

<script type="module">
// esbuild server-sent event - live reload CSS
new EventSource('/esbuild').addEventListener('change', e => { const { added, removed, updated } = JSON.parse(e.data); // reload when CSS files are added or removed if (added.length || removed.length) { location.reload(); return; } // replace updated CSS files Array.from(document.getElementsByTagName('link')).forEach(link => { const url = new URL(link.href), path = url.pathname; if (updated.includes(path) && url.host === location.host) { const css = link.cloneNode(); css.onload = () => link.remove(); css.href = `${ path }?${ +new Date() }`; link.after(css); } }) });

Tenga en cuenta que esbuild actualmente no es compatible con la recarga en caliente de JavaScript. ¡No es que confiaría en él de todos modos!

Resumen

Con un poco de configuración, esbuild podría ser suficiente para manejar todos los requisitos de construcción de desarrollo y producción de su proyecto.

Hay un conjunto completo de plugins si necesita una funcionalidad más avanzada. Tenga en cuenta que estos a menudo incluyen Sass, PostCSS o herramientas de compilación similares, por lo que usan esbuild de manera efectiva como un ejecutor de tareas. Tu siempre puedes crea tus propios complementos si necesita opciones más livianas y personalizadas.

He estado usando esbuild durante un año. La velocidad es asombrosa en comparación con paquetes similares, y aparecen nuevas funciones con frecuencia. El único inconveniente menor es romper los cambios que incurren en mantenimiento.

esbuild no pretende ser una herramienta de compilación todo en uno unificada, pero probablemente esté más cerca de ese objetivo que Roma.

punto_img

Información más reciente

punto_img