Skip to content

Le remplacement de module à chaud (HMR) vous permet de mettre à jour des modules dans une application en cours d'exécution sans avoir besoin d'un rechargement complet de la page. Cela préserve l'état de l'application et améliore l'expérience de développement.

NOTE

Le HMR est activé par défaut lors de l'utilisation du serveur de développement full-stack de Bun.

Référence de l'API import.meta.hot

Bun implémente une API HMR côté client calquée sur l'API import.meta.hot de Vite. Elle peut être vérifiée avec if (import.meta.hot), ce qui permet l'élimination de code mort en production.

ts
if (import.meta.hot) {
  // Les API HMR sont disponibles.
}

Cependant, cette vérification n'est souvent pas nécessaire car Bun éliminera le code mort de tous les appels aux API HMR dans les constructions de production.

ts
// Tout cet appel de fonction sera supprimé en production !
import.meta.hot.dispose(() => {
  console.log("dispose");
});

NOTE

L'API HMR est toujours en cours de développement. Certaines fonctionnalités sont manquantes. Le HMR peut être désactivé dans `Bun.serve` en définissant l'option development à `{ hmr: false }`.

Méthodes de l'API

MéthodeStatutNotes
hot.accept()Indique qu'une mise à jour à chaud peut être remplacée gracieusement.
hot.dataPersiste les données entre les évaluations de module.
hot.dispose()Ajoute une fonction de rappel exécutée lorsqu'un module est sur le point d'être remplacé.
hot.invalidate()
hot.on()Attache un écouteur d'événement
hot.off()Supprime un écouteur d'événement de on.
hot.send()
hot.prune()🚧NOTE : Le rappel n'est actuellement jamais appelé.
hot.decline()No-op pour correspondre à import.meta.hot de Vite

import.meta.hot.accept()

La méthode accept() indique qu'un module peut être remplacé à chaud. Lorsqu'elle est appelée sans arguments, elle indique que ce module peut être remplacé simplement en réévaluant le fichier. Après une mise à jour à chaud, les importateurs de ce module seront automatiquement corrigés.

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

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

import.meta.hot.accept();

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

Cela crée une limite de rechargement à chaud pour tous les fichiers que index.ts importe. Cela signifie que chaque fois que foo.ts ou l'une de ses dépendances est enregistré, la mise à jour remontera jusqu'à index.ts qui sera réévalué. Les fichiers qui importent index.ts seront ensuite corrigés pour importer la nouvelle version de getNegativeCount(). Si seul index.ts est mis à jour, seul ce fichier sera réévalué et le compteur dans foo.ts est réutilisé.

Cela peut être utilisé en combinaison avec import.meta.hot.data pour transférer l'état du module précédent vers le nouveau.

Avec rappel

Lorsqu'un rappel est fourni, import.meta.hot.accept fonctionnera comme dans Vite. Au lieu de corriger les importateurs de ce module, il appellera le rappel avec le nouveau module.

ts
export const count = 0;

import.meta.hot.accept(newModule => {
  if (newModule) {
    // newModule est undefined lorsqu'une SyntaxError se produit
    console.log("mis à jour : count est maintenant ", newModule.count);
  }
});

Accepter d'autres modules

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

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

  console.log("mis à jour : count est maintenant ", count);
});

Indique qu'un module de dépendance peut être accepté. Lorsque la dépendance est mise à jour, le rappel sera appelé avec le nouveau module.

Avec plusieurs dépendances

ts
import.meta.hot.accept(["./foo", "./bar"], newModules => {
  // newModules est un tableau où chaque élément correspond au module mis à jour
  // ou undefined si ce module avait une erreur de syntaxe
});

Indique que les modules de plusieurs dépendances peuvent être acceptés. Cette variante accepte un tableau de dépendances, où le rappel recevra les modules mis à jour, et undefined pour ceux qui avaient des erreurs.

import.meta.hot.data

import.meta.hot.data maintient l'état entre les instances de module pendant le remplacement à chaud, permettant le transfert de données des versions précédentes vers les nouvelles. Lorsque import.meta.hot.data est écrit, Bun marquera également ce module comme capable de s'auto-accepter (équivalent à appeler import.meta.hot.accept()).

tsx
import { createRoot } from "react-dom/client";
import { App } from "./app";

const root = (import.meta.hot.data.root ??= createRoot(elem));
root.render(<App />); // réutiliser une racine existante

En production, data est inline pour être {}, ce qui signifie qu'il ne peut pas être utilisé comme support d'état.

import.meta.hot.dispose()

Attache un rappel on-dispose. Ceci est appelé :

  • Juste avant que le module ne soit remplacé par une autre copie (avant que le suivant ne soit chargé)
  • Après que le module est détaché (suppression de toutes les importations vers ce module, voir import.meta.hot.prune())
ts
const sideEffect = setupSideEffect();

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

Retourner une promesse retardera le remplacement du module jusqu'à ce que le module soit disposé. Tous les rappels dispose sont appelés en parallèle.

import.meta.hot.prune()

Attache un rappel on-prune. Ceci est appelé lorsque toutes les importations vers ce module sont supprimées, mais que le module était précédemment chargé.

Cela peut être utilisé pour nettoyer les ressources qui ont été créées lorsque le module a été chargé. Contrairement à import.meta.hot.dispose(), cela correspond beaucoup mieux avec accept et data pour gérer les ressources avec état. Un exemple complet gérant un WebSocket :

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

// Initialiser ou réutiliser une connexion WebSocket
export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));

// Si l'import du module est supprimé, nettoyer la connexion WebSocket.
import.meta.hot.prune(() => {
  ws.close();
});

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

on() et off() sont utilisés pour écouter les événements du runtime HMR. Les noms d'événements sont préfixés avec un préfixe pour que les plugins n'entrent pas en conflit les uns avec les autres.

ts
import.meta.hot.on("bun:beforeUpdate", () => {
  console.log("avant une mise à jour à chaud");
});

Lorsqu'un fichier est remplacé, tous ses écouteurs d'événements sont automatiquement supprimés.

Événements intégrés

ÉvénementÉmis quand
bun:beforeUpdateavant qu'une mise à jour à chaud ne soit appliquée.
bun:afterUpdateaprès qu'une mise à jour à chaud soit appliquée.
bun:beforeFullReloadavant qu'un rechargement complet de page ne se produise.
bun:beforePruneavant que les rappels prune ne soient appelés.
bun:invalidatelorsqu'un module est invalidé avec import.meta.hot.invalidate()
bun:errorlorsqu'une erreur de construction ou d'exécution se produit
bun:ws:disconnectlorsque la connexion WebSocket HMR est perdue. Cela peut indiquer que le serveur de développement est hors ligne.
bun:ws:connectlorsque le WebSocket HMR se connecte ou se reconnecte.

NOTE

Pour la compatibilité avec Vite, les événements ci-dessus sont également disponibles via le préfixe `vite:*` au lieu de `bun:*`.

Bun édité par www.bunjs.com.cn