Skip to content

Hot Module Replacement (HMR) ermöglicht es Ihnen, Module in einer laufenden Anwendung zu aktualisieren, ohne ein vollständiges Neuladen der Seite zu benötigen. Dies erhält den Anwendungszustand und verbessert die Entwicklungserfahrung.

NOTE

HMR ist standardmäßig aktiviert, wenn Sie Buns Fullstack-Entwicklungsserver verwenden.

import.meta.hot API-Referenz

Bun implementiert eine clientseitige HMR-API, die nach Vites import.meta.hot API modelliert ist. Sie kann mit if (import.meta.hot) überprüft werden, wodurch sie in der Produktion durch Tree-Shaking entfernt wird.

index.ts
ts
if (import.meta.hot) {
  // HMR-APIs sind verfügbar.
}

Diese Überprüfung ist jedoch oft nicht erforderlich, da Bun Aufrufe aller HMR-APIs in Produktions-Builds durch Dead-Code-Eliminierung entfernt.

index.ts
ts
// Dieser gesamte Funktionsaufruf wird in der Produktion entfernt!
import.meta.hot.dispose(() => {
  console.log("dispose");
});

NOTE

Die HMR-API ist noch in Arbeit. Einige Funktionen fehlen. HMR kann in `Bun.serve` deaktiviert werden, indem die development-Option auf `{ hmr: false }` gesetzt wird.

API-Methoden

MethodeStatusHinweise
hot.accept()Zeigt an, dass ein Hot-Update sanft ersetzt werden kann.
hot.dataPersistiert Daten zwischen Modulauswertungen.
hot.dispose()Fügt eine Callback-Funktion hinzu, die ausgeführt wird, wenn ein Modul ersetzt wird.
hot.invalidate()
hot.on()Fügt einen Event-Listener hinzu
hot.off()Entfernt einen Event-Listener von on.
hot.send()
hot.prune()🚧HINWEIS: Callback wird derzeit nie aufgerufen.
hot.decline()No-op, um Vites import.meta.hot zu entsprechen

import.meta.hot.accept()

Die accept()-Methode zeigt an, dass ein Modul hot-ersetzt werden kann. Wenn sie ohne Argumente aufgerufen wird, zeigt sie an, dass dieses Modul einfach durch erneutes Auswerten der Datei ersetzt werden kann. Nach einem Hot-Update werden Importeure dieses Moduls automatisch gepatcht.

index.ts
ts
// index.ts
import { getCount } from "./foo.ts";

console.log("count ist ", getCount());

import.meta.hot.accept();

export function getNegativeCount() {
  return -getCount();
}

Dies erstellt eine Hot-Reloading-Grenze für alle Dateien, die index.ts importiert. Das bedeutet, dass immer dann, wenn foo.ts oder eine seiner Abhängigkeiten gespeichert wird, das Update bis zu index.ts hochblasen und neu ausgewertet wird. Dateien, die index.ts importieren, werden dann gepatcht, um die neue Version von getNegativeCount() zu importieren. Wenn nur index.ts aktualisiert wird, wird nur die eine Datei neu ausgewertet, und der Zähler in foo.ts wird wiederverwendet.

Dies kann in Kombination mit import.meta.hot.data verwendet werden, um den Zustand vom vorherigen Modul auf das neue zu übertragen.

Mit Callback

Wenn ein Callback bereitgestellt wird, funktioniert import.meta.hot.accept so wie in Vite. Anstatt die Importeure dieses Moduls zu patchen, wird es den Callback mit dem neuen Modul aufrufen.

index.ts
ts
export const count = 0;

import.meta.hot.accept(newModule => {
  if (newModule) {
    // newModule ist undefined, wenn ein Syntaxfehler aufgetreten ist
    console.log("aktualisiert: count ist jetzt ", newModule.count);
  }
});

Andere Module akzeptieren

index.ts
ts
import { count } from "./foo";

import.meta.hot.accept("./foo", () => {
  if (!newModule) return;

  console.log("aktualisiert: count ist jetzt ", count);
});

Zeigt an, dass das Modul einer Abhängigkeit akzeptiert werden kann. Wenn die Abhängigkeit aktualisiert wird, wird der Callback mit dem neuen Modul aufgerufen.

Mit mehreren Abhängigkeiten

index.ts
ts
import.meta.hot.accept(["./foo", "./bar"], newModules => {
  // newModules ist ein Array, bei dem jedes Element dem aktualisierten Modul entspricht
  // oder undefined, wenn dieses Modul einen Syntaxfehler hatte
});

Zeigt an, dass Module mehrerer Abhängigkeiten akzeptiert werden können. Diese Variante akzeptiert ein Array von Abhängigkeiten, wobei der Callback die aktualisierten Module und undefined für alle erhält, die Fehler hatten.

import.meta.hot.data

import.meta.hot.data behält den Zustand zwischen Modulinstanzen während des Hot-Ersetzens bei und ermöglicht die Datenübertragung von vorherigen zu neuen Versionen. Wenn in import.meta.hot.data geschrieben wird, markiert Bun dieses Modul auch als fähig zur Selbstakzeptierung (entspricht dem Aufruf von import.meta.hot.accept()).

index.tsx
tsx
import { createRoot } from "react-dom/client";
import { App } from "./app";

const root = (import.meta.hot.data.root ??= createRoot(elem));
root.render(<App />); // eine bestehende root wiederverwenden

In der Produktion wird data zu {} inline eingefügt, was bedeutet, dass es nicht als Zustandshalter verwendet werden kann.

import.meta.hot.dispose()

Fügt einen On-Dispose-Callback an. Dieser wird aufgerufen:

  • Kurz bevor das Modul durch eine andere Kopie ersetzt wird (bevor das nächste geladen wird)
  • Nachdem das Modul getrennt wurde (Entfernen aller Importe zu diesem Modul, siehe import.meta.hot.prune())
index.ts
ts
const sideEffect = setupSideEffect();

import.meta.hot.dispose(() => {
  sideEffect.cleanup();
});

Die Rückgabe eines Promises verzögert den Modulersatz, bis das Modul entsorgt wird. Alle Dispose-Callbacks werden parallel aufgerufen.

import.meta.hot.prune()

Fügt einen On-Prune-Callback an. Dieser wird aufgerufen, wenn alle Importe zu diesem Modul entfernt werden, das Modul jedoch zuvor geladen wurde.

Dies kann verwendet werden, um Ressourcen zu bereinigen, die beim Laden des Moduls erstellt wurden. Im Gegensatz zu import.meta.hot.dispose() passt dies viel besser zu accept und data, um zustandsbehaftete Ressourcen zu verwalten. Ein vollständiges Beispiel zur Verwaltung eines WebSocket:

index.ts
ts
import { something } from "./something";

// Eine WebSocket-Verbindung initialisieren oder wiederverwenden
export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));

// Wenn der Import des Moduls entfernt wird, die WebSocket-Verbindung bereinigen.
import.meta.hot.prune(() => {
  ws.close();
});

import.meta.hot.on() und off()

on() und off() werden verwendet, um auf Events vom HMR-Runtime zu lauschen. Event-Namen werden mit einem Präfix versehen, damit Plugins nicht miteinander in Konflikt geraten.

index.ts
ts
import.meta.hot.on("bun:beforeUpdate", () => {
  console.log("vor einem Hot-Update");
});

Wenn eine Datei ersetzt wird, werden alle ihre Event-Listener automatisch entfernt.

Integrierte Events

EventWird ausgelöst, wenn
bun:beforeUpdatebevor ein Hot-Update angewendet wird.
bun:afterUpdatenachdem ein Hot-Update angewendet wird.
bun:beforeFullReloadbevor ein vollständiges Seitenneuladen erfolgt.
bun:beforePrunebevor Prune-Callbacks aufgerufen werden.
bun:invalidatewenn ein Modul mit import.meta.hot.invalidate() ungültig gemacht wird
bun:errorwenn ein Build- oder Laufzeitfehler auftritt
bun:ws:disconnectwenn die HMR-WebSocket-Verbindung verloren geht. Dies kann anzeigen, dass der Entwicklungsserver offline ist.
bun:ws:connectwenn die HMR-WebSocket-Verbindung sich verbindet oder erneut verbindet.

NOTE

Für Kompatibilität mit Vite sind die obigen Events auch über das `vite:*`-Präfix anstelle von `bun:*` verfügbar.

Bun von www.bunjs.com.cn bearbeitet