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

import.meta.hot API 参考

Bun 实现了一个客户端 HMR API,模型参考了 Vite 的 import.meta.hot API。你可以用 if (import.meta.hot) 检查,在生产环境中它会被 tree-shake 掉。
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 自动修补模块的导入者。
index.ts
这会为 index.ts 导入的所有文件创建一个热重载边界。每当 foo.ts 或其任何依赖项被保存时,更新会冒泡到 index.ts,后者会重新评估。导入 index.ts 的文件然后会被修补以导入新版本的 getNegativeCount()。如果只有 index.ts 被更新,那么只有这个文件被重新评估,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
在生产环境中,data 被内联为 {},这意味着它不能作为状态持有者使用。
这种模式推荐用于有状态的模块,因为 Bun 可以将 {}.prop ??= value 压缩为生产环境中的 value

import.meta.hot.dispose()

附加一个 dispose 回调。这在以下情况下被调用:
  • 在模块被另一个副本替换之前(下一个加载之前)
  • 在模块被分离之后(移除了对此模块的所有导入,见 import.meta.hot.prune()
index.ts
此回调在路由导航或浏览器标签关闭时不会被调用。
返回一个 Promise 会延迟模块替换,直到模块被 dispose。所有 dispose 回调并行调用。

import.meta.hot.prune()

附加一个 prune 回调。当对此模块的所有导入都被移除时调用,但该模块之前被加载过。 用于清理模块加载时创建的资源。与 import.meta.hot.dispose() 不同,它更适合与 acceptdata 配合使用来管理有状态的资源。一个管理 WebSocket 的完整示例:
index.ts
如果改用 dispose,WebSocket 会在每次热更新时关闭并重新打开。两种版本的代码都能防止导入的文件更新时页面重载。

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

使用 on()off() 监听 HMR 运行时的事件。事件名称带有前缀,使插件不会相互冲突。
index.ts
当一个文件被替换时,其所有事件监听器会自动移除。

内置事件

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