Skip to content

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.

index.ts
ts
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.

index.ts
ts
// 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 ​

MetodoStatusNote
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
ts
// 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.

index.ts
ts
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 ​

index.ts
ts
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 ​

index.ts
ts
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()).

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 />); // ri-usa un root esistente

In 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())
index.ts
ts
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:

index.ts
ts
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.

index.ts
ts
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 ​

EventoEmesso quando
bun:beforeUpdateprima che un hot update sia applicato.
bun:afterUpdatedopo che un hot update è applicato.
bun:beforeFullReloadprima che un ricaricamento completo pagina avvenga.
bun:beforePruneprima che le callback prune siano chiamate.
bun:invalidatequando un modulo è invalidato con import.meta.hot.invalidate()
bun:errorquando un errore build o runtime avviene
bun:ws:disconnectquando la connessione WebSocket HMR è persa. Questo può indicare che il server di sviluppo è offline.
bun:ws:connectquando 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:*`.

Bun a cura di www.bunjs.com.cn