如何用Bun启动支持HMR的开发服务器?对比Webpack、Vite
用Bun实现带HMR的开发服务器(类似Vite/Webpack Serve)
1. 基础配置:指定端口与静态文件服务
Bun.serve支持直接在代码里指定端口,也可以通过环境变量动态配置。先写一个能返回入口HTML的基础服务器:
// dev-server.ts import { serve } from "bun"; import { readFileSync } from "fs"; // 优先读环境变量,默认3000端口 const PORT = process.env.BUN_PORT || 3000; serve({ port: PORT, fetch(req) { const url = new URL(req.url); // 返回入口HTML if (url.pathname === "/") { const html = readFileSync("./index.html", "utf8"); return new Response(html, { headers: { "Content-Type": "text/html" } }); } // 返回编译后的JS(后续会用Bun.build生成) if (url.pathname === "/dist/main.js") { const js = "console.log('Hello Bun HMR')"; return new Response(js, { headers: { "Content-Type": "application/javascript" } }); } return new Response("Not Found", { status: 404 }); }, }); console.log(`Dev server running at http://localhost:${PORT}`);
2. 整合Bun.build处理入口点
要像Vite那样指定入口文件编译,可在服务器启动/文件变化时调用Bun.build,把编译结果暂存后返回给客户端:
// dev-server.ts import { serve, build } from "bun"; import { readFileSync, watch } from "fs"; const PORT = process.env.BUN_PORT || 3000; let compiledJS = ""; // 编译入口文件的函数 async function compileEntry() { const result = await build({ entrypoints: ["./src/main.ts"], outdir: "./dist", sourcemap: "inline", target: "browser", minify: false, // 开发环境不压缩 }); if (!result.success) throw new Error("Build failed"); compiledJS = readFileSync("./dist/main.js", "utf8"); } // 监听src目录变化,自动重新编译 watch("./src", async () => { await compileEntry(); // 后续加HMR通知逻辑 }); // 服务器启动前先完成首次编译 await compileEntry(); serve({ port: PORT, fetch(req) { const url = new URL(req.url); if (url.pathname === "/") { const html = readFileSync("./index.html", "utf8"); return new Response(html, { headers: { "Content-Type": "text/html" } }); } if (url.pathname === "/dist/main.js") { return new Response(compiledJS, { headers: { "Content-Type": "application/javascript" } }); } return new Response("Not Found", { status: 404 }); }, }); console.log(`Dev server running at http://localhost:${PORT}`);
配套的入口文件:
<!-- index.html --> <!DOCTYPE html> <html> <head> <title>Bun Dev Server</title> </head> <body> <script type="module" src="/dist/main.js"></script> </body> </html>
// src/main.ts console.log("Hello from Bun entry!"); // 提前加入HMR支持代码 if (import.meta.hot) { import.meta.hot.accept((newModule) => { console.log("HMR updated:", newModule); // 这里可添加业务代码的更新逻辑,比如重新渲染组件 }); }
3. 实现完整HMR
Bun的HMR需要客户端+服务器配合,通过WebSocket推送更新通知:
修改dev-server.ts,加入WebSocket支持:
// dev-server.ts import { serve, build } from "bun"; import { readFileSync, watch } from "fs"; const PORT = process.env.BUN_PORT || 3000; let compiledJS = ""; let wsConnections: WebSocket[] = []; async function compileEntry() { const result = await build({ entrypoints: ["./src/main.ts"], outdir: "./dist", sourcemap: "inline", target: "browser", minify: false, define: { "process.env.NODE_ENV": '"development"' }, }); if (!result.success) throw new Error("Build failed"); compiledJS = readFileSync("./dist/main.js", "utf8"); // 编译完成后通知所有客户端更新 wsConnections.forEach(ws => ws.send("update")); } watch("./src", async () => { await compileEntry(); }); await compileEntry(); serve({ port: PORT, fetch(req) { const url = new URL(req.url); // 处理WebSocket连接 if (url.pathname === "/ws") { const upgrade = req.headers.get("upgrade"); if (upgrade === "websocket") { const { socket, response } = Bun.serveWebSocket({ async open(ws) { wsConnections.push(ws); ws.addEventListener("close", () => { wsConnections = wsConnections.filter(conn => conn !== ws); }); }, }); return response; } } // 处理HTML和JS if (url.pathname === "/") { const html = readFileSync("./index.html", "utf8"); return new Response(html, { headers: { "Content-Type": "text/html" } }); } if (url.pathname === "/dist/main.js") { return new Response(compiledJS, { headers: { "Content-Type": "application/javascript" } }); } return new Response("Not Found", { status: 404 }); }, }); console.log(`Dev server running at http://localhost:${PORT}`);
更新客户端代码,监听WebSocket更新:
// src/main.ts console.log("Hello from Bun entry!"); // HMR模块更新逻辑 if (import.meta.hot) { import.meta.hot.accept((newModule) => { console.log("HMR updated:", newModule); }); } // 连接WebSocket监听文件变化 const ws = new WebSocket(`ws://localhost:${location.port}/ws`); ws.addEventListener("message", async (event) => { if (event.data === "update") { // 通知Bun的HMR系统失效当前模块并重新加载 import.meta.hot?.invalidate(); const newModule = await import("./main.ts?update=" + Date.now()); console.log("Reloaded module:", newModule); } });
4. 启动与端口配置
- 在package.json中添加启动脚本:
{ "scripts": { "dev": "bun run dev-server.ts" } } - 指定端口的两种方式:
- 直接修改dev-server.ts中的
PORT变量 - 通过环境变量启动:
BUN_PORT=3001 bun run dev
- 直接修改dev-server.ts中的
运行bun run dev后,打开http://localhost:3000即可编写JS代码,修改src/main.ts后浏览器会自动完成热更新,无需手动刷新。
内容的提问来源于stack exchange,提问作者Robin Schambach
相关产品推荐
相关产品推荐

