【Bun中文文档-Runtime】Watch mode

439 阅读2分钟

Bun 支持两种通过 CLI 标志自动重新加载的方式:

  • --watch 模式,当导入的文件更改时,硬重启 Bun 的进程。
  • --hot 模式,当导入的文件更改时,对代码进行软重载(不重新启动进程)。

--watch 模式

可以在bun test或运行 TypeScript、JSX 和 JavaScript 文件时使用监视模式。

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

$ bun --watch index.tsx

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

$ bun --watch test

--watch模式下,Bun 会跟踪所有导入的文件并监视它们的更改。当检测到更改时,Bun 会重新启动进程,保留了初始运行中使用的相同一组 CLI 参数和环境变量。如果 Bun 崩溃,--watch将尝试自动重新启动进程。

⚡️ 重新加载很快。 您可能已经习惯了文件系统监视器,它们有几层库包装了本机 API,或者更糟糕的是依赖于轮询。

相比之下,Bun 使用操作系统本机文件系统监视器 API,如 kqueue 或 inotify 来检测文件的更改。Bun 还进行了一些优化,以使其适用于更大的项目(例如设置高文件描述符的 rlimit,静态分配文件路径缓冲区,尽可能复用文件描述符等)。

以下示例显示了 Bun 在编辑文件时实时重新加载文件,VSCode 配置为在每次按键时保存文件。

$ bun run --watch watchy.tsx
import { serve } from "bun";
console.log("I restarted at:", Date.now());

serve({
  port: 4003,

  fetch(request) {
    return new Response("Sup");
  },
});

bun watch gif

在监视模式下运行bun test和启用save-on-keypress

$ bun --watch test

bun test gif

--hot 模式

使用bun --hot来在 Bun 中执行代码时启用热重载。

$ bun --hot server.ts

从入口点开始(上面示例中的server.ts),Bun 构建了所有导入的源文件的注册表(不包括node_modules中的文件),并监视它们的更改。当检测到更改时,Bun 执行"软重载"。所有文件都会重新评估,但所有全局状态(特别是globalThis对象)都会保留。

// 让TypeScript高兴
declare global {
  var count: number;
}

globalThis.count ??= 0;
console.log(`Reloaded ${globalThis.count} times`);
globalThis.count++;

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

如果使用bun --hot server.ts运行此文件,您将看到每次保存文件时重新加载计数递增。

$ bun --hot index.ts
Reloaded 1 times
Reloaded 2 times
Reloaded 3 times

传统的文件监视器(如nodemon)会重新启动整个进程,因此 HTTP 服务器和其他有状态的对象会丢失。相比之下,bun --hot能够在不重新启动进程的情况下反映更新的代码。

HTTP 服务器

Bun 提供了以下简化的 API 来实现 HTTP 服务器。有关详细信息,请参阅API > HTTP

import {serve} from "bun";

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

serve({
  fetch(req: Request) {
    return new Response(`Reloaded ${globalThis.count} times`);
  },
  port: 3000,
});

上面的文件只是导出了一个带有定义的fetch处理程序的对象。当执行此文件时,Bun 将其解释为 HTTP 服务器,并将导出的对象传递给Bun.serve

当您保存文件时,HTTP 服务器将以更新的代码重新加载,而不重新启动进程。这导致刷新速度非常快。

Bun与Nodemon刷新速度比较

注意 — 在 Bun 的未来版本中,计划支持 Vite 的import.meta.hot,以实现更好的热重载的生命周期管理,并与生态系统对齐。

实现细节

在热重载时,Bun:

  • 重置内部的require缓存和 ES 模块注册表(Loader.registry
  • 同步运行垃圾收集器(以最小化内存泄漏,以牺牲运行时性能为代价)
  • 从头重新编译所有代码(包括 sourcemap)
  • 使用 JavaScriptCore 重新评估代码

这个实现并不特别优化。它会重新编译没有更改的文件。它不尝试增量编译。这只是一个起点。