Was sind Bundler?

Ganz egal, ob wir es merken oder nicht, wenn wir heute ein neues Web-Projekten starten, werden wir ziemlich sicher einen "Bundler" benutzen. Meistens müssen wir uns auch gar nicht großartig damit beschäftigen, was der Bundler so tut, irgendwann kommt aber der Punkt, wo ein tieferes Verständnis hilfreich sein kann. Wir wollen darauf eingehen, wieso Bundler in der Web-Welt quasi allgegenwärtig sind, was ihre Aufgabe ist, und wie sie unsere tägliche Entwicklung beeinflussen.

Hintergrundgeschichte - von 2000-Zeilen-Files, zu "Task Runnern" zu Bundlern

Um zu verstehen, wo Bundler her kommen, müssen wir ca. 20 Jahre in die Vergangenheit springen. Damals haben wir Webprojekte mit der folgenden Ordnerstruktur umgesetzt:

/project
- assets/
  - main.css
  - main.js
  - jquery.js
- index.html
- contact.html
- about-us.html

Wir hatten also eine Hand voll HTML-Dateien und verlinken von dort wenige statische Assets, wie zum Beispiel eine globale CSS-Datei oder ausgewählte JavaScript-Bibliotheken, wie jQuery.

Mit steigender Komplexität der Projekte wurde die Dateistruktur auch immer komplexer. Mehr Seiten, Code der geteilt werden soll, Code der nur auf einer Seite ausgeführt werden soll und viele weitere spannende Anwendungsfälle.

Task Runner, um unseren Code zu transformieren

Da wurde schnell klar, dass wir nicht mehr nur mit einer JavaScript-Datei bzw. einer CSS-Datei arbeiten wollen. Also haben wir angefangen, unseren Code auf mehrere Dateien aufzuteilen. Allerdings mussten wir früher (vor HTTP 2) für jede Datei einen separate Anfrage an unseren Server schicken, sodass jede weitere Datei einen gewissen Overhead hatte. Also fingen wir an, Tools zu entwickeln, die uns alle CSS-Dateien im Projekt sammeln und zu einer großen Datei verbinden. So waren die Task Runner, wie z.B. Gulp geboren. Diese Tools vollbrachten ihr Werk jedoch mit recht wenig Intelligenz und packten einfach meist sämtliche Dateien aus dem Projekt aneinander und komprimierten sie vielleicht noch, sodass Nutzer immerhin schon weniger Kilobytes herunterladen mussten.

Mit diesem Schritt war jedoch eine Barriere durchbrochen: Der Code, den wir im Projekt geschrieben schreiben mussten, war nicht mehr zwingend der gleiche Code, der im Browser ausgeführt wird. Wir hatten also schon die Gelegenheit, uns das Leben mit Tools leichter zu machen, ohne, dass die Browser oder die Nutzer etwas davon mitbekommen mussten. Es folgte eine rasante Entwicklung von Bibliotheken, die niemals zur Laufzeit ausgeführt werden sollten, wie z.B. die folgenden:

  1. Less, um CSS schreiben einfacher zu machen
  2. CoffeeScript, um JS schreiben einfacher zu machen
  3. Browserify, was uns erstmals ermöglichte, Abhängigkeiten zwischen Dateien und Bibliotheken auch im Browser nutzbar machen zu können

Browserify war hier besonders spannend: In dem die require()-Aufrufe (Vorgänger der heutigen ES Modules und der import-Funktionalität) aus unserem Source Code noch bei uns auf dem Rechner analysiert werden, konnten auf bestimmte Imports bestimmte Tools angewendet werden, um zum Beispiel moderne JavaScript-Syntax zu einer Syntax zu konvertieren, die auch der Browser versteht. Und dieser Ansatz weitete sich aus.

JavaScript im Zentrum der Webentwicklung

JavaScript rutschte immer mehr in die Mitte der Webentwicklung. Die Build-Tools wurden mit JavaScript geschrieben, wir nutzen JavaScript, um unsere Transformations-Schritte des Source Codes zu definieren, und wir definieren unsere Pakete und Abhängigkeiten in JavaScript. Tools, die diese Abhängigkeiten verstehen, wurden immer mächtiger. Daraus entstanden dann die ersten Vollwertigen Bundler, wie Webpack oder Vite. Diese nahmen dann als Einstiegspunkt ein JavaScript file, und analysierten sämtliche Imports, die von unserem Einstiegsfile gemacht wurden. Sie unterstützen dann auch schnell andere Dateiformate, wie Bilder, CSS-Dateien oder Schriftarten, sodass der Bundler wirklich die Kontrolle über jedes File hatte, welches am Ende zum Nutzer ausgespielt werden soll.

Heute sieht eine gängige Projektstruktur so aus:

/project
- src/
  - components/
    - Button.js
    - Button.css
    - ButtonIcon.png
  - App.js 
- dist/
  - bundle.js
  - a23b23c.png
  - bundle.css

Wir haben also unseren Ordner mit Source-Code, schieben diesen durch die Bundler-Maschinerie und erhalten einen schlanken dist für "distribution" oder build Ordner, der dann von unserem Webserver an Nutzer ausgespielt wird.

Bundler in der Entwicklung

Heute benutzen wir Bundler nicht nur für das Ausliefern der Dateien an unsere Endnutzer. Auch in der Entwicklung starten wir Tools wie Vite, die unseren Source Code anschauen. Sie enthalten inzwischen auch direkt Webserver, die unseren Code für den Browser verfügbar machen, sodass wir die Dateien nicht mehr direkt aus dem Dateisystem, sondern direkt über die localhost-URL öffnen können. Zudem transformieren sie unsere Dateien nur noch, wenn sie wirklich benötigt werden und beschleunigen so Entwicklungszyklen, da nicht mehr das ganze Projekt fertig gebaut und optimiert sein muss, bevor ich Ergebnisse im Browser sehen kann.

Fazit: Bundler sind "gekommen, um zu bleiben"

Eine Welt ohne Bundler ist inzwischen kaum noch vorstellbar. Wir genießen so viele Vorteile durch Tools wie Tailwind oder TypeScript, die alle darauf basieren, unseren Code zu analysieren und zu transformieren, bevor er im Browser ausgeführt werden kann. Dadurch ist vollkommen klar, dass der Bundler uns auch in Zukunft ein steter Begleiter sein wird, auch wenn die Tools vielleicht mehr und mehr "unsichtbar" werden, weil sie in der IDE oder hinter Bibliotheken wie Next.js versteckt sind.