Skip to main content
热模块替换(HMR)可以在不重新加载整个页面的情况下更新运行中应用程序的模块,同时保留应用程序状态。
在使用 Bun 全栈开发服务器时,HMR 默认启用。

import.meta.hot API 参考

Bun 实现了一个客户端 HMR API,其设计参考了 Vite 的 import.meta.hot API。你可以通过 if (import.meta.hot) 检查它,在生产环境中该代码会被摇树优化移除。
index.ts
通常不需要进行此检查,因为 Bun 会在生产构建中消除对所有 HMR API 的调用。
index.ts
为了使这项死代码消除功能生效,Bun 会强制要求不通过间接方式调用这些 API。这意味着以下方式不可行:
index.ts
HMR API 仍在开发中,部分功能尚未实现。要在 Bun.serve 中禁用 HMR,请将 development 选项设置为 { hmr: false }。

API 方法

import.meta.hot.accept()

accept() 方法表示模块可以进行热替换。不带参数调用时,表示 Bun 可以通过重新求值该文件来替换此模块。热更新后,Bun 会自动修补该模块的导入者。
index.ts
此调用会为 index.ts 导入的所有文件创建热重载边界。每当你保存 foo.ts 或其任何依赖项时,更新都会向上冒泡到 index.ts,然后重新求值。接着,Bun 会修补导入 index.ts 的文件,使其导入新版 getNegativeCount()。如果你只更新 index.ts,Bun 只会重新求值该文件,并复用 foo.ts 中的计数器。 将此功能与 import.meta.hot.data 结合使用,可以将状态从之前的模块传递到新模块。
如果没有模块调用 import.meta.hot.accept()(并且没有 React Fast Refresh 或插件代你调用),文件更新时页面会重新加载。控制台警告会显示哪些文件失效了。如果依赖完整页面重新加载更合适,可以放心忽略此警告。

带回调

传入回调时,import.meta.hot.accept 的行为与在 Vite 中相同。它不会修补此模块的导入者,而是使用新模块调用回调。
index.ts
通常建议调用不带参数的 import.meta.hot.accept(),这样代码会更清晰。

接受其它模块

index.ts
表示可以接受某个依赖项的模块。依赖项更新时,Bun 会使用新模块调用回调。

多依赖

index.ts
此变体接受一个依赖项数组。回调会收到一个数组,其中更新后的模块位于对应索引处,其它依赖项对应的位置为 undefined。

import.meta.hot.data

import.meta.hot.data 会在热替换过程中,将模块先前版本的状态传递给新版本。向 import.meta.hot.data 写入内容也会将模块标记为自行接受(等同于调用 import.meta.hot.accept())。
index.tsx
在生产环境中,Bun 会将 data 内联为 {},因此不能将其用作状态持有器。
对于有状态模块,我们建议使用此模式,因为 Bun 可以在生产环境中将 {}.prop ??= value 缩小为 value。

import.meta.hot.dispose()

添加一个卸载回调。Bun 会在以下情况调用它:
  • 模块即将被替换(即新模块加载前)
  • 模块被卸载(所有对该模块的导入被移除,见 import.meta.hot.prune())
index.ts
Bun 不会在路由导航或浏览器标签页关闭时调用此回调。
返回一个 promise 会延迟模块替换,直到模块完成卸载。Bun 会并行调用所有卸载回调。

import.meta.hot.prune()

添加一个清理回调。当该模块的所有导入都被移除,但模块之前已加载时,Bun 会调用它。 可以使用它来清理模块加载时创建的资源。与 import.meta.hot.dispose() 不同,它与 accept 和 data 配合管理有状态资源时更加合适。以下是一个管理 WebSocket 的完整示例:
index.ts
如果改用 dispose,WebSocket 会在每次热更新时关闭并重新打开。这两个版本的代码在更新导入的文件时,都会阻止页面重新加载。

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

使用 on() 和 off() 监听来自 HMR 运行时的事件。事件名称带有前缀,因此插件之间不会发生冲突。
index.ts
替换文件时,Bun 会自动移除该文件的所有事件监听器。

内置事件

为了兼容 Vite,这些事件也可以使用 vite:* 前缀,而不是 bun:* 前缀。