Skip to content

Hot Module Replacement (HMR) permite que você atualize módulos em uma aplicação em execução sem precisar de um recarregamento completo da página. Isso preserva o estado da aplicação e melhora a experiência de desenvolvimento.

NOTE

HMR é habilitado por padrão ao usar o servidor de desenvolvimento full-stack do Bun.

Referência da API import.meta.hot

O Bun implementa uma API HMR do lado do cliente modelada após a API import.meta.hot do Vite. Ela pode ser verificada com if (import.meta.hot), fazendo tree-shaking em produção.

index.ts
ts
if (import.meta.hot) {
  // APIs HMR estão disponíveis.
}

No entanto, esta verificação geralmente não é necessária pois o Bun irá eliminar código morto de todas as chamadas às APIs HMR em builds de produção.

index.ts
ts
// Esta chamada de função inteira será removida em produção!
import.meta.hot.dispose(() => {
  console.log("dispose");
});

NOTE

A API HMR ainda é um trabalho em andamento. Alguns recursos estão faltando. HMR pode ser desabilitado em `Bun.serve` definindo a opção development para `{ hmr: false }`.

Métodos da API

MétodoStatusNotas
hot.accept()Indica que uma atualização hot pode ser substituída graciosamente.
hot.dataPersiste dados entre avaliações de módulo.
hot.dispose()Adiciona uma função callback para executar quando um módulo está prestes a ser substituído.
hot.invalidate()
hot.on()Anexa um event listener
hot.off()Remove um event listener do on.
hot.send()
hot.prune()🚧NOTA: Callback nunca é chamado atualmente.
hot.decline()No-op para corresponder ao import.meta.hot do Vite

import.meta.hot.accept()

O método accept() indica que um módulo pode ser hot-replaced. Quando chamado sem argumentos, indica que este módulo pode ser substituído simplesmente reavaliando o arquivo. Após uma atualização hot, os importadores deste módulo serão automaticamente corrigidos.

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

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

import.meta.hot.accept();

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

Isso cria uma fronteira de hot-reloading para todos os arquivos que index.ts importa. Isso significa que sempre que foo.ts ou qualquer uma de suas dependências forem salvas, a atualização irá subir até index.ts ser reavaliado. Arquivos que importam index.ts serão então corrigidos para importar a nova versão de getNegativeCount(). Se apenas index.ts for atualizado, apenas um arquivo será reavaliado e o contador em foo.ts é reutilizado.

Isso pode ser usado em combinação com import.meta.hot.data para transferir estado do módulo anterior para o novo.

Com callback

Quando fornecido um callback, import.meta.hot.accept funcionará como funciona no Vite. Em vez de corrigir os importadores deste módulo, ele chamará o callback com o novo módulo.

index.ts
ts
export const count = 0;

import.meta.hot.accept(newModule => {
  if (newModule) {
    // newModule é undefined quando ocorre SyntaxError
    console.log("updated: count is now ", newModule.count);
  }
});

Aceitando outros módulos

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

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

  console.log("updated: count is now ", count);
});

Indica que o módulo de uma dependência pode ser aceito. Quando a dependência é atualizada, o callback será chamado com o novo módulo.

Com múltiplas dependências

index.ts
ts
import.meta.hot.accept(["./foo", "./bar"], newModules => {
  // newModules é um array onde cada item corresponde ao módulo atualizado
  // ou undefined se aquele módulo teve um erro de sintaxe
});

Indica que os módulos de múltiplas dependências podem ser aceitos. Esta variante aceita um array de dependências, onde o callback receberá os módulos atualizados e undefined para qualquer um que teve erros.

import.meta.hot.data

import.meta.hot.data mantém estado entre instâncias de módulo durante substituição hot, habilitando transferência de dados de versões anteriores para novas. Quando import.meta.hot.data é escrito, o Bun também marcará este módulo como capaz de auto-aceitar (equivalente a chamar 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 />); // reutiliza um root existente

Em produção, data é inline para {}, significando que não pode ser usado como um holder de estado.

import.meta.hot.dispose()

Anexa um callback on-dispose. Isso é chamado:

  • Logo antes do módulo ser substituído por outra cópia (antes do próximo ser carregado)
  • Após o módulo ser desanexado (removendo todos os imports para este módulo, veja import.meta.hot.prune())
index.ts
ts
const sideEffect = setupSideEffect();

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

Retornar uma promise irá atrasar a substituição do módulo até que o módulo seja descartado. Todos os callbacks dispose são chamados em paralelo.

import.meta.hot.prune()

Anexa um callback on-prune. Isso é chamado quando todos os imports para este módulo são removidos, mas o módulo foi previamente carregado.

Isso pode ser usado para limpar recursos que foram criados quando o módulo foi carregado. Diferentemente de import.meta.hot.dispose(), isso combina muito melhor com accept e data para gerenciar recursos stateful. Um exemplo completo gerenciando um WebSocket:

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

// Inicializa ou reutiliza uma conexão WebSocket
export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));

// Se o import do módulo for removido, limpa a conexão WebSocket.
import.meta.hot.prune(() => {
  ws.close();
});

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

on() e off() são usados para ouvir eventos do runtime HMR. Nomes de eventos são prefixados com um prefixo para que plugins não entrem em conflito uns com os outros.

index.ts
ts
import.meta.hot.on("bun:beforeUpdate", () => {
  console.log("before a hot update");
});

Quando um arquivo é substituído, todos os seus event listeners são automaticamente removidos.

Eventos built-in

EventEmitido quando
bun:beforeUpdateantes de uma atualização hot ser aplicada.
bun:afterUpdateapós uma atualização hot ser aplicada.
bun:beforeFullReloadantes de um recarregamento completo da página acontecer.
bun:beforePruneantes dos callbacks prune serem chamados.
bun:invalidatequando um módulo é invalidado com import.meta.hot.invalidate()
bun:errorquando ocorre um erro de build ou runtime
bun:ws:disconnectquando a conexão WebSocket HMR é perdida. Isso pode indicar que o servidor de desenvolvimento está offline.
bun:ws:connectquando o WebSocket HMR conecta ou reconecta.

NOTE

Para compatibilidade com Vite, os eventos acima também estão disponíveis via prefixo `vite:*` em vez de `bun:*`.

Bun by www.bunjs.com.cn edit