Bun unter der Haube

Zig, JavaScriptCore und warum Bun so schnell ist

6 Minuten
Bun unter der Haube
#Bun #JavaScript #TypeScript #Zig

Bun ist mehr als eine Node.js-Alternative. Die Runtime kombiniert Zig als Systemsprache mit Apples JavaScriptCore-Engine und erreicht damit Performance-Werte, die klassische JavaScript-Runtimes nicht bieten können. Ein Blick auf die Architektur zeigt, warum.

Zig und JavaScriptCore

Während Node.js auf V8 (Chrome) setzt, verwendet Bun JavaScriptCore aus Safari. Der entscheidende Unterschied liegt aber tiefer: Buns Event Loop, Task Scheduler und native I/O-Operationen sind direkt in Zig implementiert.

Was das bringt:

  • Bis zu 4x schnellere Startzeiten
  • Niedrigere Latenz bei I/O-Operationen
  • Optimierte Module-Resolution und Caching

Zig ermöglicht direkten Speicherzugriff ohne Garbage-Collection-Overhead. Das macht sich besonders bei häufigen, kleinen Operationen bemerkbar – genau das, was Server-Anwendungen ständig tun.

Plugin-System

Buns Bundler ist über Plugins erweiterbar. Entwickler können in den Build-Prozess eingreifen und eigene Loader definieren:

import type { BunPlugin } from "bun";

const textPlugin: BunPlugin = {
  name: "text-loader",
  setup(build) {
    build.onLoad({ filter: /\.txt$/ }, async (args) => {
      const text = await Bun.file(args.path).text();
      return {
        contents: `export default ${JSON.stringify(text)}`,
        loader: "js",
      };
    });
  },
};

Mit build.onLoad lassen sich beliebige Dateitypen zu JavaScript-Modulen transformieren. Das ermöglicht:

  • Custom Loader für proprietäre Formate
  • Preprocessing von Assets
  • Lazy Loading für Fullstack-Anwendungen

Tree-Shaking und Minification sind dabei automatisch integriert.

Tree-Shaking im Detail

Buns Bundler analysiert Code statisch und entfernt ungenutzte Exports durch Dead-Code-Elimination – standardmäßig aktiviert.

Bei ESM-Modulen:

// foo.js
export const foo = () => "foo";
export const bar = () => "bar";

// main.js
import { foo } from "./foo.js";
// bar wird automatisch entfernt

Der Bundler erkennt, welche Symbole tatsächlich referenziert werden und schließt nur diese ein.

Bei CommonJS: Dynamische Exports wie module.exports = require(...) verhindern statische Analyse. In diesen Fällen bleibt das gesamte exports-Objekt erhalten. Bun konvertiert CommonJS bei Bedarf zu ESM und injiziert minimalen Wrapper-Code.

Build-Konfiguration:

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  target: "bun",
  minify: true,
  env: "inline", // Tree-Shaking für Umgebungsvariablen
});

Native SQLite-Integration

Das bun:sqlite-Modul ist direkt in die Runtime integriert – kein npm-Paket, keine nativen Bindings:

import { Database } from "bun:sqlite";

const db = new Database("app.db");

// Prepared Statements
const query = db.prepare("SELECT * FROM users WHERE id = ?");
const user = query.get(1);

// Transactions
db.transaction(() => {
  db.run("INSERT INTO users (name) VALUES (?)", ["Alice"]);
  db.run("INSERT INTO users (name) VALUES (?)", ["Bob"]);
})();

Performance-Vorteile:

  • 3-6x schneller als better-sqlite3
  • Prepared Statements mit Caching
  • Named und Positional Parameters
  • Automatische Datatype-Conversions (BLOB → Uint8Array)

Für Low-Level-Zugriff gibt es db.fileControl mit direktem Zugang zu SQLite3-APIs. Results lassen sich auf Klassen mappen für type-safe Queries.

Native APIs

Bun bringt APIs mit, die über Standard-JavaScript hinausgehen:

Foreign Function Interface:

import { dlopen, FFIType } from "bun:ffi";

const lib = dlopen("libcrypto.so", {
  MD5: {
    args: [FFIType.ptr, FFIType.u64, FFIType.ptr],
    returns: FFIType.ptr,
  },
});

Memory-Mapped Files:

const file = Bun.file("large-data.bin");
const mapped = Bun.mmap(file);

Diese APIs ermöglichen Optimierungen, die mit Node.js nur über native Addons möglich wären.

Edge-Deployment

Für Edge-Szenarien kombiniert Bun gut mit Frameworks wie Hono:

import { Hono } from "hono";

const app = new Hono();

app.get("/", (c) => c.json({ message: "Hello from the edge" }));

export default app;

Buns schnelle Startzeiten und niedriger Memory-Footprint machen es ideal für:

  • Cloudflare Workers
  • Fly.io
  • Serverless-Funktionen

Die Empfehlung: Stateless Logic und minimale Dependencies priorisieren.


Quellen