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 会自动修补该模块的导入者。
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()

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

import.meta.hot.prune()

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

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

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

内置事件

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