You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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"
      }
    }
    
  • 指定端口的两种方式:
    1. 直接修改dev-server.ts中的PORT变量
    2. 通过环境变量启动:BUN_PORT=3001 bun run dev

运行bun run dev后,打开http://localhost:3000即可编写JS代码,修改src/main.ts后浏览器会自动完成热更新,无需手动刷新。

内容的提问来源于stack exchange,提问作者Robin Schambach

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 03:42:50