Hot Module Replacement (HMR) ti permette di aggiornare moduli in un'applicazione in esecuzione senza bisogno di un ricaricamento completo della pagina. Questo preserva lo stato dell'applicazione e migliora l'esperienza di sviluppo.
NOTE
HMR è abilitato di default quando si usa il server di sviluppo full-stack di Bun.Riferimento API import.meta.hot
Bun implementa un'API HMR client-side modellata sull'API import.meta.hot di Vite. Può essere verificata con if (import.meta.hot), che la rimuove con tree-shaking in produzione.
if (import.meta.hot) {
// Le API HMR sono disponibili.
}Tuttavia, questo controllo è spesso non necessario poiché Bun eliminerà con dead-code-elimination le chiamate a tutte le API HMR nelle build di produzione.
// Questa intera chiamata funzione sarà rimossa in produzione!
import.meta.hot.dispose(() => {
console.log("dispose");
});NOTE
L'API HMR è ancora un work in progress. Alcune funzionalità mancano. HMR può essere disabilitato in `Bun.serve` impostando l'opzione development a `{ hmr: false }`.Metodi API
| Metodo | Status | Note |
|---|---|---|
hot.accept() | ✅ | Indica che un hot update può essere rimpiazzato graziosamente. |
hot.data | ✅ | Persiste dati tra valutazioni di moduli. |
hot.dispose() | ✅ | Aggiunge una callback da eseguire quando un modulo sta per essere rimpiazzato. |
hot.invalidate() | ❌ | |
hot.on() | ✅ | Aggiunge un listener eventi |
hot.off() | ✅ | Rimuove un listener eventi da on. |
hot.send() | ❌ | |
hot.prune() | 🚧 | NOTA: La callback non è attualmente mai chiamata. |
hot.decline() | ✅ | No-op per corrispondere a import.meta.hot di Vite |
import.meta.hot.accept()
Il metodo accept() indica che un modulo può essere hot-replaced. Quando chiamato senza argomenti, indica che questo modulo può essere rimpiazzato semplicemente rivalutando il file. Dopo un hot update, gli importatori di questo modulo saranno automaticamente patchati.
// index.ts
import { getCount } from "./foo.ts";
console.log("count è ", getCount());
import.meta.hot.accept();
export function getNegativeCount() {
return -getCount();
}Questo crea un boundary hot-reloading per tutti i file che index.ts importa. Questo significa che ogni volta che foo.ts o qualsiasi sua dipendenza è salvata, l'aggiornamento salirà fino a index.ts che sarà rivalutato. I file che importano index.ts saranno poi patchati per importare la nuova versione di getNegativeCount(). Se solo index.ts è aggiornato, solo quel file sarà rivalutato, e il counter in foo.ts è riutilizzato.
Questo può essere usato in combinazione con import.meta.hot.data per trasferire stato dal modulo precedente a quello nuovo.
Con callback
Quando fornita una callback, import.meta.hot.accept funzionerà come fa in Vite. Invece di patchare gli importatori di questo modulo, chiamerà la callback con il nuovo modulo.
export const count = 0;
import.meta.hot.accept(newModule => {
if (newModule) {
// newModule è undefined quando è avvenuto un SyntaxError
console.log("aggiornato: count è ora ", newModule.count);
}
});Accettare altri moduli
import { count } from "./foo";
import.meta.hot.accept("./foo", () => {
if (!newModule) return;
console.log("aggiornato: count è ora ", count);
});Indica che il modulo di una dipendenza può essere accettato. Quando la dipendenza è aggiornata, la callback sarà chiamata con il nuovo modulo.
Con multiple dipendenze
import.meta.hot.accept(["./foo", "./bar"], newModules => {
// newModules è un array dove ogni elemento corrisponde al modulo aggiornato
// o undefined se quel modulo aveva un errore di sintassi
});Indica che i moduli di multiple dipendenze possono essere accettati. Questa variante accetta un array di dipendenze, dove la callback riceverà i moduli aggiornati, e undefined per quelli che avevano errori.
import.meta.hot.data
import.meta.hot.data mantiene stato tra istanze di moduli durante hot replacement, abilitando trasferimento dati da versioni precedenti a nuove. Quando import.meta.hot.data è scritto, Bun marcherà anche questo modulo come capace di auto-accettazione (equivalente a chiamare 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 />); // ri-usa un root esistenteIn produzione, data è inlinato per essere {}, il che significa che non può essere usato come state holder.
import.meta.hot.dispose()
Aggiunge una callback on-dispose. Questa è chiamata:
- Appena prima che il modulo sia rimpiazzato con un'altra copia (prima che il prossimo sia caricato)
- Dopo che il modulo è staccato (rimuovendo tutti gli import a questo modulo, vedi
import.meta.hot.prune())
const sideEffect = setupSideEffect();
import.meta.hot.dispose(() => {
sideEffect.cleanup();
});Restituire una promise ritarderà il rimpiazzamento del modulo finché il modulo non è disposed. Tutte le callback dispose sono chiamate in parallelo.
import.meta.hot.prune()
Aggiunge una callback on-prune. Questa è chiamata quando tutti gli import a questo modulo sono rimossi, ma il modulo era precedentemente caricato.
Questo può essere usato per pulire risorse che sono state create quando il modulo era caricato. A differenza di import.meta.hot.dispose(), questo si abbina molto meglio con accept e data per gestire risorse stateful. Un esempio completo che gestisce un WebSocket:
import { something } from "./something";
// Inizializza o ri-usa una connessione WebSocket
export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));
// Se l'import del modulo è rimosso, pulisci la connessione WebSocket.
import.meta.hot.prune(() => {
ws.close();
});import.meta.hot.on() e off()
on() e off() sono usati per ascoltare eventi dall'runtime HMR. I nomi eventi sono prefissati con un prefisso così che i plugin non vadano in conflitto tra loro.
import.meta.hot.on("bun:beforeUpdate", () => {
console.log("prima di un hot update");
});Quando un file è rimpiazzato, tutti i suoi listener eventi sono automaticamente rimossi.
Eventi built-in
| Evento | Emesso quando |
|---|---|
bun:beforeUpdate | prima che un hot update sia applicato. |
bun:afterUpdate | dopo che un hot update è applicato. |
bun:beforeFullReload | prima che un ricaricamento completo pagina avvenga. |
bun:beforePrune | prima che le callback prune siano chiamate. |
bun:invalidate | quando un modulo è invalidato con import.meta.hot.invalidate() |
bun:error | quando un errore build o runtime avviene |
bun:ws:disconnect | quando la connessione WebSocket HMR è persa. Questo può indicare che il server di sviluppo è offline. |
bun:ws:connect | quando il WebSocket HMR si connette o ri-connette. |
NOTE
Per compatibilità con Vite, gli eventi sopra sono anche disponibili tramite prefisso `vite:*` invece di `bun:*`.