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.
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.
// 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
| Methode | Status | Hinweise |
|---|---|---|
hot.accept() | ✅ | Zeigt an, dass ein Hot-Update sanft ersetzt werden kann. |
hot.data | ✅ | Persistiert 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
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.
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
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
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()).
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 wiederverwendenIn 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())
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:
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.
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
| Event | Wird ausgelöst, wenn |
|---|---|
bun:beforeUpdate | bevor ein Hot-Update angewendet wird. |
bun:afterUpdate | nachdem ein Hot-Update angewendet wird. |
bun:beforeFullReload | bevor ein vollständiges Seitenneuladen erfolgt. |
bun:beforePrune | bevor Prune-Callbacks aufgerufen werden. |
bun:invalidate | wenn ein Modul mit import.meta.hot.invalidate() ungültig gemacht wird |
bun:error | wenn ein Build- oder Laufzeitfehler auftritt |
bun:ws:disconnect | wenn die HMR-WebSocket-Verbindung verloren geht. Dies kann anzeigen, dass der Entwicklungsserver offline ist. |
bun:ws:connect | wenn 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.