name: Bun热重载 description: 在使用Bun实现热重载时使用,包括–hot、–watch、HMR或开发期间的自动代码重载。涵盖监视模式、热模式和HTTP服务器重载。 version: 1.0.0
Bun热重载
Bun提供内置的热重载功能,以加快开发周期。
监视模式 vs 热模式
| 特性 | --watch |
--hot |
|---|---|---|
| 行为 | 重启进程 | 重载模块 |
| 状态 | 重载时丢失 | 保留 |
| 速度 | ~20ms重启 | 即时重载 |
| 使用场景 | 任何文件类型 | Bun.serve HTTP |
监视模式 (–watch)
当文件变化时重启整个进程。
# 基本监视模式
bun --watch run src/index.ts
# 监视特定脚本
bun --watch run dev
# 监视测试运行器
bun --watch test
package.json脚本
{
"scripts": {
"dev": "bun --watch run src/index.ts",
"dev:server": "bun --watch run src/server.ts",
"test:watch": "bun --watch test"
}
}
监视行为
- 自动监视导入的文件
- 触发任何
.ts、.tsx、.js、.jsx变化 - 也监视
.json导入 - 用新状态重启
热模式 (–hot)
无需重启进程,原地重载模块。
bun --hot run src/server.ts
HTTP服务器热重载
// src/server.ts
let counter = 0; // 状态在热重载间保留
export default {
port: 3000,
fetch(req: Request) {
counter++;
return new Response(`请求 #${counter}`);
},
};
bun --hot run src/server.ts
当修改server.ts时,模块即时重载,而counter保持其值。
Bun.serve 热重载
// src/server.ts
const server = Bun.serve({
port: 3000,
fetch(req) {
return new Response("你好!");
},
});
// 热重载处理器
if (import.meta.hot) {
import.meta.hot.accept(() => {
console.log("热重载!");
});
}
console.log(`服务器运行在端口 ${server.port}`);
import.meta.hot API
// 检查热重载是否可用
if (import.meta.hot) {
// 接受此模块的更新
import.meta.hot.accept();
// 带回调接受
import.meta.hot.accept((newModule) => {
console.log("模块更新:", newModule);
});
// 重载前清理
import.meta.hot.dispose(() => {
// 关闭连接、清除间隔等
clearInterval(myInterval);
});
// 拒绝热重载(强制完全重启)
import.meta.hot.decline();
// 使此模块无效(触发父模块重载)
import.meta.hot.invalidate();
}
HTTP服务器模式
Express-like模式
// src/server.ts
import { createApp } from "./app";
const app = createApp();
const server = Bun.serve({
port: 3000,
fetch: app.fetch,
});
// 热重载:重新创建应用
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// 使用新的fetch处理器重载
server.reload({
fetch: newModule.default.fetch,
});
});
}
有状态服务器
// src/server.ts
// 存储在globalThis中以在重载后存活
globalThis.connections ??= new Set();
const server = Bun.serve({
port: 3000,
fetch(req) {
return new Response(`连接数:${globalThis.connections.size}`);
},
websocket: {
open(ws) {
globalThis.connections.add(ws);
},
close(ws) {
globalThis.connections.delete(ws);
},
},
});
if (import.meta.hot) {
import.meta.hot.accept();
}
自定义监视实现
// dev-server.ts
import { watch } from "fs";
const srcDir = "./src";
let server: ReturnType<typeof Bun.serve> | null = null;
async function startServer() {
// 带缓存清除的动态导入
const module = await import(`./src/server.ts?t=${Date.now()}`);
if (server) {
server.stop();
}
server = Bun.serve(module.default);
console.log(`服务器启动在端口 ${server.port}`);
}
// 初始启动
await startServer();
// 监视变化
watch(srcDir, { recursive: true }, async (event, filename) => {
if (filename?.endsWith(".ts") || filename?.endsWith(".tsx")) {
console.log(`
[${event}] ${filename}`);
await startServer();
}
});
console.log("监视变化中...");
WebSocket实时重载
服务器
// src/dev-server.ts
const clients = new Set<ServerWebSocket>();
const server = Bun.serve({
port: 3000,
fetch(req, server) {
if (req.headers.get("upgrade") === "websocket") {
server.upgrade(req);
return;
}
// 在开发中注入重载脚本
const html = `
<!DOCTYPE html>
<html>
<body>
<h1>你好!</h1>
<script>
const ws = new WebSocket('ws://localhost:3000');
ws.onmessage = (e) => {
if (e.data === 'reload') location.reload();
};
</script>
</body>
</html>
`;
return new Response(html, {
headers: { "Content-Type": "text/html" },
});
},
websocket: {
open(ws) {
clients.add(ws);
},
close(ws) {
clients.delete(ws);
},
},
});
// 文件变化时通知客户端
watch("./src", { recursive: true }, () => {
clients.forEach((ws) => ws.send("reload"));
});
Vite集成
用于前端开发与HMR:
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
hmr: true,
},
});
# 使用Bun运行Vite
bunx --bun vite
测试与监视
# 监视测试
bun --watch test
# 监视特定文件
bun --watch test src/utils.test.ts
# 带bail(首次失败停止)
bun --watch test --bail
环境检测
// 检查是否以--hot运行
const isHot = !!import.meta.hot;
// 检查是否以--watch运行
const isWatch = process.env.BUN_WATCH === "1";
// 开发模式
const isDev = process.env.NODE_ENV !== "production";
if (isDev) {
console.log("运行在开发模式");
console.log(`热重载:${isHot}`);
console.log(`监视模式:${isWatch}`);
}
常见问题
状态未保留
// ❌ 热重载时状态丢失
let cache = new Map();
// ✅ 热重载时状态保留
globalThis.cache ??= new Map();
const cache = globalThis.cache;
清理未运行
// ❌ 重载后间隔继续运行
setInterval(() => console.log("tick"), 1000);
// ✅ 在dispose时清理
const interval = setInterval(() => console.log("tick"), 1000);
if (import.meta.hot) {
import.meta.hot.dispose(() => {
clearInterval(interval);
});
}
模块未重载
// ❌ 导入未监视
const config = require("./config.json");
// ✅ 使用import进行监视
import config from "./config.json";
常见错误
| 错误 | 原因 | 修复 |
|---|---|---|
未检测到变化 |
文件未导入 | 检查导入链 |
状态丢失 |
使用--watch |
使用--hot或globalThis |
端口使用中 |
服务器未停止 | 实现server.stop() |
内存泄漏 |
无清理 | 使用dispose回调 |
何时加载参考
加载 references/advanced-hmr.md 当:
- 自定义HMR协议
- 模块联邦
- 复杂状态管理
加载 references/debugging.md 当:
- HMR不工作
- 状态问题
- 性能调试