运行时包管理器打包器测试运行器代码检查格式化指南参考博客反馈

观察模式

Bun 中的自动重载,支持 --watch 和 --hot 模式

Bun 支持两种自动重载方式:

  • --watch 模式:当导入的文件发生更改时,Bun 会硬重启进程。
  • --hot 模式:当导入的文件发生更改时,Bun 会软重载代码(不重启进程)。

--watch 模式

观察模式可与 bun test 和 bun build 配合使用,也可用于运行 TypeScript、JSX 和 JavaScript 文件。

在 --watch 模式下运行文件:

terminal
bun --watch index.tsx

在 --watch 模式下运行测试:

terminal
bun --watch test

在 --watch 模式下,Bun 会跟踪所有导入的文件并监视它们的变化。当文件发生变化时,Bun 会使用与初始运行相同的 CLI 参数和环境变量重新启动进程。如果 Bun 崩溃,--watch 会尝试重新启动进程。

以下示例展示了 Bun 如何在你编辑文件时实时重载文件,并且 VSCode 已配置为每次按键时保存文件。

terminal
bun run --watch watchy.tsx
watchy.tsx
import { serve } from "bun";

console.log("我在此时重启:", Date.now());

serve({
  port: 4003,
  fetch(request) {
    return new Response("Sup");
  },
});

bun watch 动图

启用 save-on-keypress 后,在 watch 模式下运行 bun test:

terminal
bun --watch test

bun test 动图


--hot 模式

使用 bun --hot 在使用 Bun 执行代码时启用热重载。与 --watch 模式不同,Bun 不会完全重启整个进程。它会检测代码变化,并使用新代码更新其内部模块缓存。

terminal
bun --hot server.ts

以入口文件(本例中为 server.ts)为起点,Bun 会构建一个包含所有导入源文件(不包括 node_modules 中的文件)的注册表,并监视这些文件的变化。当文件发生变化时,Bun 会执行“软重载”。Bun 会重新求值所有文件,但全局状态(尤其是 globalThis 对象)会保留。

server.ts
// 让 TypeScript 不报错
declare global {
  var count: number;
}

globalThis.count ??= 0;
console.log(`重载了 ${globalThis.count} 次`);
globalThis.count++;

// 防止 `bun run` 退出
setInterval(function () {}, 1000000);

如果你用 bun --hot server.ts 运行这个文件,每次保存文件时你都会看到重载计数递增。

terminal
bun --hot server.ts
Text
Reloaded 0 times
Reloaded 1 times
Reloaded 2 times

像 nodemon 这样的传统文件监视器会重启整个进程,因此 HTTP 服务器和其他有状态对象都会丢失。相比之下,bun --hot 无需重启进程即可反映更新后的代码。

HTTP 服务器

你可以更新 HTTP 请求处理器,而无需关闭服务器:保存文件时,Bun 会使用更新后的代码重新加载服务器,同时不会重启进程。因此可以实现极快的刷新速度。

server.ts
globalThis.count ??= 0;
globalThis.count++;

Bun.serve({
  fetch(req: Request) {
    return new Response(`重载了 ${globalThis.count} 次`);
  },
  port: 3000,
});
实现细节

热重载时,Bun 会:

  • 重置内部的 require 缓存和 ES 模块注册表(Loader.registry)
  • 同步运行垃圾回收(以减少内存泄漏,但会牺牲一定运行时性能)
  • 重新从头转译所有代码(包括源映射)
  • 使用 JavaScriptCore 重新求值代码

目前的实现并非特别优化,会重新转译未发生变化的文件,也没有尝试增量编译。这只是一个起点。