Zephyrnet-Logo

Eine Einführung in den esbuild Bundler – SitePoint

Datum:

bauen ist ein schneller Bundler, der JavaScript-, TypeScript-, JSX- und CSS-Code optimieren kann. Dieser Artikel hilft Ihnen, sich mit esbuild vertraut zu machen und zeigt Ihnen, wie Sie Ihr eigenes Build-System ohne andere Abhängigkeiten erstellen.

Inhaltsverzeichnis
  1. Wie funktioniert esbuild?
  2. Warum Bundle?
  3. Warum esbuild verwenden?
  4. Warum Esbuild vermeiden?
  5. Superschneller Start
  6. Beispielprojekt
  7. Projektübersicht
  8. Esbuild konfigurieren
  9. JavaScript-Bündelung
  10. CSS-Bündelung
  11. Beobachten, Wiederaufbauen und Dienen
  12. Zusammenfassung

Wie funktioniert esbuild?

Rahmen wie z Schraube haben esbuild übernommen, aber Sie können esbuild als eigenständiges Tool in Ihren eigenen Projekten verwenden.

  • esbuild bündelt JavaScript-Code in eine einzige Datei, ähnlich wie bei Bundlern wie z Aufrollen. Dies ist die Hauptfunktion von esbuild. Sie löst Module auf, meldet Syntaxprobleme, „Tree-Shakes“ zum Entfernen ungenutzter Funktionen, löscht Protokollierungs- und Debugger-Anweisungen, minimiert Code und stellt Quellzuordnungen bereit.

  • esbuild bündelt CSS-Code in eine einzige Datei. Es ist kein vollständiger Ersatz für Vorprozessoren wie z Sass or PostCSS, aber esbuild kann Partials, Syntaxprobleme, Verschachtelung, Inline-Asset-Kodierung, Quellzuordnungen, automatische Präfixierung und Minimierung verarbeiten. Das könnte alles sein, was Sie brauchen.

  • esbuild stellt außerdem einen lokalen Entwicklungsserver bereit mit automatischer Bündelung und Hot-Reloading, sodass keine Aktualisierung erforderlich ist. Es verfügt nicht über alle Funktionen, die von angeboten werden Browsersync, aber für die meisten Fälle ist es gut genug.

Der folgende Code hilft Ihnen, die Esbuild-Konzepte zu verstehen, damit Sie weitere Konfigurationsmöglichkeiten für Ihre Projekte untersuchen können.

Warum Bundle?

Das Bündeln von Code in einer einzigen Datei bietet verschiedene Vorteile. Hier sind einige davon:

  • Sie können kleinere, eigenständige Quelldateien entwickeln, die einfacher zu verwalten sind
  • Sie können den Code während des Bündelungsprozesses linten, verschönern und auf Syntax prüfen
  • Der Bundler kann ungenutzte Funktionen entfernen – bekannt als Baumschütteln
  • Sie können alternative Versionen desselben Codes bündeln und Ziele für ältere Browser, Node.js, Deno usw. erstellen
  • Einzelne Dateien werden schneller geladen als mehrere Dateien und der Browser benötigt keine ES-Modulunterstützung
  • Die Bündelung auf Produktionsebene kann die Leistung verbessern, indem Code minimiert und Protokollierungs- und Debugging-Anweisungen entfernt werden

Warum esbuild verwenden?

Im Gegensatz zu JavaScript-Bundlern ist esbuild eine kompilierte ausführbare Go-Datei, die eine starke Parallelverarbeitung implementiert. Es ist schnell und bis zu hundertmal schneller als Rollup, Parcel oder Webpack. Dies könnte über die gesamte Laufzeit eines Projekts Wochen an Entwicklungszeit einsparen.

Darüber hinaus bietet esbuild auch:

  • integrierte Bündelung und Kompilierung für JavaScript, TypeScript, JSX und CSS
  • Befehlszeilen-, JavaScript- und Go-Konfigurations-APIs
  • Unterstützung für ES-Module und CommonJS
  • ein lokaler Entwicklungsserver mit Überwachungsmodus und Live-Nachladen
  • Plugins um weitere Funktionalität hinzuzufügen
  • umfassende Dokumentation und ein Online-Experimentiertool

Warum Esbuild vermeiden?

Zum Zeitpunkt des Verfassens dieses Artikels hat esbuild Version 0.18 erreicht. Es ist zuverlässig, aber immer noch ein Betaprodukt.

esbuild wird häufig aktualisiert und die Optionen können sich zwischen den Versionen ändern. Der Dokumentation empfiehlt Ihnen, bei einer bestimmten Version zu bleiben. Sie können es aktualisieren, müssen jedoch möglicherweise Ihre Konfigurationsdateien migrieren und sich mit der neuen Dokumentation befassen, um wichtige Änderungen zu entdecken.

Beachten Sie auch, dass esbuild keine TypeScript-Typprüfung durchführt, Sie müssen es also trotzdem ausführen tsc -noEmit.

Superschneller Start

Erstellen Sie bei Bedarf ein neues Node.js-Projekt mit npm init, dann installieren Sie esbuild lokal als Entwicklungsabhängigkeit:

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

Die Installation benötigt ca. 9 MB. Überprüfen Sie, ob es funktioniert, indem Sie diesen Befehl ausführen, um die installierte Version anzuzeigen:

./node_modules/.bin/esbuild --version

Oder führen Sie diesen Befehl aus, um die CLI-Hilfe anzuzeigen:

./node_modules/.bin/esbuild --help

Verwenden Sie die CLI-API, um ein Eingabeskript zu bündeln (myapp.js) und alle seine importierten Module in einer einzigen Datei mit dem Namen bundle.js. esbuild gibt eine Datei im standardmäßigen, browserbasierten, sofort aufgerufenen Funktionsausdrucksformat (IIFE) aus:

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

Du kannst dich Installieren Sie esbuild auf andere Weise wenn Sie Node.js nicht verwenden.

Beispielprojekt

Laden Sie die Beispieldateien und eine herunter esbuild-Konfiguration von Github. Es handelt sich um ein Node.js-Projekt. Installieren Sie also die einzelne Esbuild-Abhängigkeit mit:

npm install

Bauen Sie die Quelldateien ein src zum build Verzeichnis und starten Sie einen Entwicklungsserver mit:

npm start

Navigiere jetzt zu localhost:8000 in Ihrem Browser, um eine Webseite mit einer Echtzeituhr anzuzeigen. Wenn Sie eine CSS-Datei aktualisieren in src/css/ or src/css/partials, esbuild wird den Code neu bündeln und die Stile live neu laden.

esbuild Beispiel-Uhrenprojekt

Presse- Strg|Befehlstaste + Strg|Befehlstaste um den Server zu stoppen.

Erstellen Sie einen Produktions-Build für die Bereitstellung mit:

npm run build

Untersuchen Sie die CSS- und JavaScript-Dateien im build Verzeichnis, um die minimierten Versionen ohne Quellkarten anzuzeigen.

Projektübersicht

Die Echtzeituhrseite ist in a aufgebaut build Verzeichnis mit Quelldateien von src.

Das package.json Datei definiert fünf npm Skripte. Der erste löscht die build Verzeichnis:

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

Bevor es zu einer Bündelung kommt, muss ein init Skript läuft clean, erstellt ein neues build Verzeichnis und Kopien:

  1. eine statische HTML-Datei von src/html/index.html zu build/index.html
  2. statische Bilder von src/images/ zu build/images/
"init": "npm run clean && mkdir ./build && cp ./src/html/* ./build/ && cp -r ./src/images ./build",

An esbuild.config.js Die Datei steuert den esbuild-Bündelungsprozess mithilfe der JavaScript-API. Dies ist einfacher zu verwalten als die Übergabe von Optionen an die CLI-API, was unhandlich werden kann. Ein npm bundle Skript läuft init gefolgt von node ./esbuild.config.js:

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

Die letzten zwei npm Skripte laufen bundle mit entweder a production or development Parameter übergeben an ./esbuild.config.js So steuern Sie den Build:

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

Wann ./esbuild.config.js ausgeführt wird, bestimmt es, ob eine Minimierung erstellt werden soll production Dateien (Standard) oder development Dateien mit automatischen Updates, Quellkarten und einem Live-Neuladeserver. In beiden Fällen sind esbuild-Bundles:

  • die Eintrags-CSS-Datei src/css/main.css zu build/css/main.css
  • die Eintrags-JavaScript-Datei scr/js/main.js zu build/js/main.js

Esbuild konfigurieren

package.json hat eine "type" of "module" so alles .js Dateien können ES-Module verwenden. Der esbuild.config.js Skript-Importe esbuild und Sätze productionMode zu true beim Bündeln für die Produktion bzw false beim Bündeln für die Entwicklung:

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`);

Bündelziel

Beachten Sie, dass die Zielvariable definiert eine Reihe von Browsern und Versionsnummern, die in der Konfiguration verwendet werden sollen. Dies wirkt sich auf die gebündelte Ausgabe aus und ändert die Syntax, um bestimmte Plattformen zu unterstützen. Esbuild kann beispielsweise:

  • erweitern native CSS-Verschachtelung in vollständige Selektoren (die Verschachtelung würde bestehen bleiben, wenn "Chrome115" war das einzige Ziel)
  • Fügen Sie bei Bedarf CSS-Herstellerpräfixe hinzu
  • polyfill die ?? nullish Koaleszenzoperator
  • entfernen # aus privaten Klassenfeldern

Neben Browsern können Sie auch gezielt darauf abzielen node und es Versionen wie es2020 und esnext (die neuesten JS- und CSS-Funktionen).

JavaScript-Bündelung

Die einfachste API zum Erstellen eines Bundles:

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

Dies repliziert den oben verwendeten CLI-Befehl:

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

Das Beispielprojekt verwendet erweiterte Optionen wie die Dateiüberwachung. Dies erfordert einen lang laufenden Build Kontext wodurch die Konfiguration festgelegt wird:


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' });

esbuild-Angebote Dutzende Konfigurationsoptionen. Hier ist eine Übersicht über die hier verwendeten:

  • entryPoints definiert ein Array von Dateieintrittspunkten für die Bündelung. Das Beispielprojekt verfügt über ein Skript unter ./src/js/main.js.

  • format legt das Ausgabeformat fest. Das Beispiel verwendet esm, aber Sie können optional festlegen iife für ältere Browser bzw commonjs für Node.js.

  • bundle einstellen true Inlines importierte Module in die Ausgabedatei.

  • target ist das oben definierte Array der Zielbrowser.

  • drop ist ein Array von console und / oder debugger Anweisungen, die entfernt werden sollen. In diesem Fall entfernen Produktions-Builds beide und Entwicklungs-Builds behalten sie bei.

  • logLevel Definiert die Ausführlichkeit der Protokollierung. Das obige Beispiel zeigt Fehler während Produktions-Builds und ausführlichere Informationsmeldungen während Entwicklungs-Builds.

  • minify Reduziert die Codegröße, indem Kommentare und Leerzeichen entfernt und Variablen und Funktionen nach Möglichkeit umbenannt werden. Das Beispielprojekt wird jedoch während der Produktionserstellung minimiert verschönert Code während der Entwicklungs-Builds.

  • sourcemap einstellen linked (nur im Entwicklungsmodus) generiert eine verknüpfte Quellkarte in einem .map Datei, damit die ursprüngliche Quelldatei und -zeile in den Browser-Entwicklertools verfügbar ist. Sie können auch einstellen inline um die Quellkarte in die gebündelte Datei einzuschließen, both beides zu schaffen, oder external generieren .map Datei ohne Link aus dem gebündelten JavaScript.

  • outdir Definiert das Ausgabeverzeichnis der gebündelten Datei.

Rufen Sie die Kontextobjekte auf rebuild() Methode zum einmaligen Ausführen des Builds – normalerweise für einen Produktionsbuild:

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

Rufen Sie die Kontextobjekte auf watch() Methode, um die Ausführung fortzusetzen und automatisch neu zu erstellen, wenn sich überwachte Dateien ändern:

await buildJS.watch();

Das Kontextobjekt stellt sicher, dass nachfolgende Builds inkrementell verarbeitet werden und dass sie Arbeit aus früheren Builds wiederverwenden, um die Leistung zu verbessern.

JavaScript-Eingabe- und Ausgabedateien

Der Eintritt src/js/main.js Dateiimporte dom.js und time.js Module aus der lib Unterordner. Es findet alle Elemente mit einer Klasse von clock und setzt ihren Textinhalt jede Sekunde auf die aktuelle Zeit:

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 exportiert zwei Funktionen. main.js importiert beide, verwendet aber nur 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 exportiert zwei Funktionen. main.js Importe formatHMS(), aber das nutzt die anderen Funktionen im Modul:

 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());
}

Das resultierende Entwicklungsbündel entfernt (Tree Shakes) get() für dom.js aber beinhaltet alles time.js Funktionen. Außerdem wird eine Quellkarte generiert:


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);
} 

(Beachten Sie, dass esbuild dies kann umschreiben let und const zu var für Korrektheit und Geschwindigkeit.)

Das resultierende Produktionspaket minimiert den Code auf 322 Zeichen:

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);

CSS-Bündelung

Die CSS-Bündelung im Beispielprojekt verwendet ein ähnliches Kontextobjekt wie JavaScript oben:


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' });

Es definiert ein external Option als Array von Dateien und Pfaden zu ausschließen aus dem Bau. Im Beispielprojekt sind Dateien im src/images/ Verzeichnis werden in das kopiert build Verzeichnis, damit HTML, CSS oder JavaScript direkt darauf verweisen können. Wenn dies nicht festgelegt wäre, würde esbuild Dateien in die Ausgabe kopieren build/css/ Verzeichnis, wenn Sie sie verwenden background-image oder ähnliche Eigenschaften.

Das loader Die Option ändert, wie esbuild mit einer importierten Datei umgeht, auf die nicht verwiesen wird external Vermögenswert. In diesem Beispiel:

  • SVG-Bilder werden als Daten-URIs eingebunden
  • PNG- und JPG-Bilder werden in kopiert build/css/ Verzeichnis gespeichert und als Dateien referenziert

CSS-Eingabe- und Ausgabedateien

Der Eintritt src/css/main.css Dateiimporte variables.css und elements.css von dem partials Unterordner:


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

variables.css definiert standardmäßige benutzerdefinierte Eigenschaften:


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

elements.css definiert alle Stile. Notiz:

  • body hat ein Hintergrundbild, das von extern geladen wurde images Verzeichnis
  • h1 ist darin verschachtelt header
  • h1 hat eine Hintergrund-SVG, die eingebunden wird
  • Die Zielbrowser erfordern keine Herstellerpräfixe

*, *::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;
}

Das resultierende Entwicklungspaket erweitert die verschachtelte Syntax, integriert die SVG-Datei und generiert eine Quellzuordnung:


: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;
} 

Das resultierende Produktionspaket minimiert den Code auf 764 Zeichen (das SVG wird hier weggelassen):

: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}

Beobachten, Wiederaufbauen und Dienen

Der Rest der esbuild.config.js Skriptpakete einmal für Produktions-Builds vor dem Beenden:

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

Während der Entwicklungs-Builds läuft das Skript weiter, sucht nach Dateiänderungen und führt automatisch ein erneutes Bundle durch. Der buildCSS context startet einen Entwicklungs-Webserver mit build/ als Stammverzeichnis:

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

Starten Sie den Entwicklungs-Build mit:

npm start

Navigieren Sie dann zu localhost:8000 um die Seite anzuzeigen.

Im Gegensatz zu Browsersync müssen Sie zum Live-Neuladen Ihren eigenen Code zu den Entwicklungsseiten hinzufügen. Bei Änderungen sendet esbuild Informationen über das Update per Vom Server gesendetes Ereignis. Die einfachste Möglichkeit besteht darin, die Seite bei jeder Änderung vollständig neu zu laden:

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

Das Beispielprojekt verwendet das CSS-Kontextobjekt, um den Server zu erstellen. Das liegt daran, dass ich JavaScript-Änderungen lieber manuell aktualisiere – und weil ich für esbuild keine Möglichkeit gefunden habe, ein Ereignis sowohl für CSS- als auch für JS-Updates zu senden! Die HTML-Seite enthält das folgende Skript zum Ersetzen aktualisierter CSS-Dateien ohne eine vollständige Seitenaktualisierung (Hot-Reload):

<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); } }) });

Beachten Sie, dass esbuild derzeit kein Hot-Reloading von JavaScript unterstützt – Ich würde dem sowieso nicht vertrauen!

Zusammenfassung

Mit ein wenig Konfiguration könnte esbuild ausreichen, um alle Entwicklungs- und Produktions-Build-Anforderungen Ihres Projekts zu erfüllen.

Es gibt einen umfassenden Satz von Plugins Sollten Sie erweiterte Funktionen benötigen? Beachten Sie, dass diese häufig Sass, PostCSS oder ähnliche Build-Tools umfassen, sodass sie esbuild effektiv als Task-Runner verwenden. Du kannst immer Erstellen Sie Ihre eigenen Plugins wenn Sie leichtere, individuelle Optionen benötigen.

Ich benutze esbuild seit einem Jahr. Die Geschwindigkeit ist im Vergleich zu ähnlichen Bundlern erstaunlich und es erscheinen häufig neue Funktionen. Der einzige kleine Nachteil sind bahnbrechende Änderungen, die Wartungsaufwand erfordern.

esbuild erhebt nicht den Anspruch, ein einheitliches All-in-One-Build-Tool zu sein, aber es kommt diesem Ziel wahrscheinlich näher als Rom.

spot_img

Neueste Intelligenz

spot_img