Skip to content

El Reemplazo de Módulos en Caliente (HMR) te permite actualizar módulos en una aplicación en ejecución sin necesidad de recargar completamente la página. Esto preserva el estado de la aplicación y mejora la experiencia de desarrollo.

NOTE

HMR está habilitado por defecto al usar el servidor de desarrollo full-stack de Bun.

Referencia de la API import.meta.hot

Bun implementa una API HMR del lado del cliente modelada según la API import.meta.hot de Vite. Puede verificarse con if (import.meta.hot), eliminándolo del bundle en producción.

index.ts
ts
if (import.meta.hot) {
  // Las APIs de HMR están disponibles.
}

Sin embargo, esta verificación a menudo no es necesaria ya que Bun eliminará las llamadas a todas las APIs de HMR en compilaciones de producción.

index.ts
ts
// ¡Toda esta llamada de función se eliminará en producción!
import.meta.hot.dispose(() => {
  console.log("dispose");
});

NOTE

La API HMR aún es un trabajo en progreso. Algunas características faltan. HMR puede deshabilitarse en `Bun.serve` estableciendo la opción development en `{ hmr: false }`.

Métodos de la API

MétodoEstadoNotas
hot.accept()Indica que una actualización en caliente puede reemplazarse gradualmente.
hot.dataPersiste datos entre evaluaciones de módulos.
hot.dispose()Agrega una función callback que se ejecuta cuando un módulo está a punto de ser reemplazado.
hot.invalidate()
hot.on()Adjunta un listener de eventos
hot.off()Elimina un listener de eventos de on.
hot.send()
hot.prune()🚧NOTA: El callback actualmente nunca se llama.
hot.decline()No-op para coincidir con import.meta.hot de Vite

import.meta.hot.accept()

El método accept() indica que un módulo puede ser reemplazado en caliente. Cuando se llama sin argumentos, indica que este módulo puede reemplazarse simplemente re-evaluando el archivo. Después de una actualización en caliente, los importadores de este módulo se parchearán automáticamente.

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

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

import.meta.hot.accept();

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

Esto crea un límite de recarga en caliente para todos los archivos que index.ts importa. Eso significa que cada vez que foo.ts o cualquiera de sus dependencias se guarden, la actualización subirá hasta index.ts y se re-evaluará. Los archivos que importan index.ts se parchearán para importar la nueva versión de getNegativeCount(). Si solo se actualiza index.ts, solo ese archivo se re-evaluará, y el contador en foo.ts se reutiliza.

Esto puede usarse en combinación con import.meta.hot.data para transferir estado del módulo anterior al nuevo.

Con callback

Cuando se proporciona un callback, import.meta.hot.accept funcionará como lo hace en Vite. En lugar de parchear los importadores de este módulo, llamará al callback con el nuevo módulo.

index.ts
ts
export const count = 0;

import.meta.hot.accept(newModule => {
  if (newModule) {
    // newModule es undefined cuando ocurre un SyntaxError
    console.log("actualizado: count ahora es ", newModule.count);
  }
});

Aceptar otros módulos

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

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

  console.log("actualizado: count ahora es ", count);
});

Indica que el módulo de una dependencia puede ser aceptado. Cuando la dependencia se actualiza, el callback se llamará con el nuevo módulo.

Con múltiples dependencias

index.ts
ts
import.meta.hot.accept(["./foo", "./bar"], newModules => {
  // newModules es un array donde cada elemento corresponde al módulo actualizado
  // o undefined si ese módulo tuvo un error de sintaxis
});

Indica que los módulos de múltiples dependencias pueden ser aceptados. Esta variante acepta un array de dependencias, donde el callback recibirá los módulos actualizados, y undefined para cualquiera que tenga errores.

import.meta.hot.data

import.meta.hot.data mantiene el estado entre instancias de módulos durante el reemplazo en caliente, permitiendo transferencia de datos de versiones anteriores a nuevas. Cuando se escribe en import.meta.hot.data, Bun también marcará este módulo como capaz de auto-aceptarse (equivalente a llamar 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 />); // re-utilizar un root existente

En producción, data se incluye en línea como {}, lo que significa que no puede usarse como contenedor de estado.

import.meta.hot.dispose()

Adjunta un callback on-dispose. Esto se llama:

  • Justo antes de que el módulo sea reemplazado con otra copia (antes de que el siguiente se cargue)
  • Después de que el módulo es desvinculado (eliminando todos los imports a este módulo, ver import.meta.hot.prune())
index.ts
ts
const sideEffect = setupSideEffect();

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

Devolver una promesa retrasará el reemplazo del módulo hasta que el módulo sea eliminado. Todos los callbacks dispose se llaman en paralelo.

import.meta.hot.prune()

Adjunta un callback on-prune. Esto se llama cuando todos los imports a este módulo son eliminados, pero el módulo fue cargado previamente.

Esto puede usarse para limpiar recursos que fueron creados cuando el módulo fue cargado. A diferencia de import.meta.hot.dispose(), esto se combina mucho mejor con accept y data para gestionar recursos con estado. Un ejemplo completo gestionando un WebSocket:

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

// Inicializar o re-utilizar una conexión WebSocket
export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));

// Si el import del módulo es eliminado, limpiar la conexión WebSocket.
import.meta.hot.prune(() => {
  ws.close();
});

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

on() y off() se usan para escuchar eventos del runtime HMR. Los nombres de eventos están prefijados con un prefijo para que los plugins no entren en conflicto entre sí.

index.ts
ts
import.meta.hot.on("bun:beforeUpdate", () => {
  console.log("antes de una actualización en caliente");
});

Cuando un archivo es reemplazado, todos sus listeners de eventos son automáticamente eliminados.

Eventos integrados

EventoSe emite cuando
bun:beforeUpdateantes de que se aplique una actualización en caliente.
bun:afterUpdatedespués de que se aplique una actualización en caliente.
bun:beforeFullReloadantes de que ocurra una recarga completa de página.
bun:beforePruneantes de que se llamen los callbacks prune.
bun:invalidatecuando un módulo es invalidado con import.meta.hot.invalidate()
bun:errorcuando ocurre un error de construcción o runtime
bun:ws:disconnectcuando la conexión WebSocket HMR se pierde. Esto puede indicar que el servidor de desarrollo está offline.
bun:ws:connectcuando el WebSocket HMR se conecta o re-conecta.

NOTE

Para compatibilidad con Vite, los eventos anteriores también están disponibles vía el prefijo `vite:*` en lugar de `bun:*`.

Bun por www.bunjs.com.cn editar