首页 / Bun 入门教程 / HTML 与全栈开发服务器

Bun 入门教程

HTML 与全栈开发服务器

本教程共 34 篇 · 第 20 篇 · 更新于 2026-08-06

Bun打包器HTML全栈热重载Hot Reload

本节目标:

  • 理解 Bun 如何把 HTML 文件当作打包入口(开发服务器与生产构建)
  • 掌握 bun --hot 全栈开发模式与 HMR 热重载
  • 学会用 Bun.serve({ routes }) 把 HTML 导入作为路由,并处理 API 请求
  • 了解动态路由、独立 HTML 打包与开发/生产两种工作流

前几章我们一直在”打包出 JS/CSS 产物”的语境下讨论打包器。但真实的前端/全栈开发里,第一公民往往是 HTML:一个 index.html 引用了 CSS、JS、图片,我们希望”改一行代码、浏览器自动刷新”。Bun 把这件事做得很轻量——你几乎不需要 webpack-dev-server 那样的重型配置,直接用 bun 命令就能跑起带热重载的开发服务器,甚至能用同一个文件描述”前端页面 + 后端 API”的全栈应用。

20.1 把 HTML 当作开发入口

Bun 可以直接”运行”一个 HTML 文件,此时它会启动一个开发服务器,把该 HTML 及其引用的资源(JS、CSS、图片)按依赖图实时服务出来:

# 启动开发服务器,自动服务 index.html
bun ./index.html

执行后终端会打印一个本地地址(如 http://localhost:3000),浏览器打开即可。当你修改 index.html 引用的 TS/CSS 文件时,Bun 会重新构建相关模块。这种方式对 SPA(单页应用)、MPA(多页应用,多个 HTML 入口)都适用,多页时把多个 HTML 路径一起作为参数即可。

Note

在第 17 章提到的 file loader、CSS 导入等,在这里会自然生效:HTML 里 <link> 的本地 CSS、<script> 引用的 TS,都会被 Bun 的依赖图纳入构建并按需服务。

20.2 生产构建 HTML

开发用 bun ./index.html 方便,但上线要用 bun build 产出静态文件:

# 把 index.html 及其资源打包到 dist
bun build ./index.html --outdir ./dist --minify

# 多页应用:传入多个 HTML 入口
bun build ./pages/home.html ./pages/about.html --outdir ./dist

生产构建会把 HTML 里引用的脚本、样式、图片全部处理:本地图片 URL 会被加上内容哈希、CSS 的 @import 会被合并、TS/JSX 会被转译。最终 ./dist 目录是一份可直接交给任意静态服务器托管的完整站点。

Tip

部署前加 --minify 压缩产物,并用 --sourcemap=linked 保留排错能力(第 19 章已讲)。HTML 入口同样可以享受这些优化。

20.3 把 HTML 应用编译成单文件可执行文件

如果你希望把整个 HTML 应用(含其 JS/CSS/图片资源)随一个文件一起分发,可以用 --compile

# 把 HTML 应用编译成单一可执行文件,资源随二进制内嵌
bun build --compile ./index.html --outfile ./myapp

--compile 会把 HTML 引用的脚本、样式、图片等资源全部内嵌进生成的二进制文件(机制见第 19 章),拿到文件的机器无需安装 Bun 即可直接运行。这种方式适合做演示工具或离线分发的客户端。

20.4 bun --hot:全栈热重载

--hot(或 -h)模式是 Bun 开发体验的核心。它让服务器在文件变化时自动重载,且对前端做 HMR(热模块替换)——只更新变化的模块,不丢失页面状态:

# 启动带热重载的全栈开发服务器
bun --hot ./server.ts

bun --hot 会监听项目文件,当源码变更时,自动重启或热替换相关模块,浏览器侧无需手动刷新即可看到更新。这对”改后端 API 顺带改前端页面”的全栈开发尤为顺手。

Note

热重载依赖 import.meta.hot API(详见下文 20.6)。在生产构建里,这类 HMR 代码会被 dead-code-eliminate 自动移除,不会进入产物。

20.5 用 Bun.serve 把 HTML 作为路由

Bun 的全栈模式下,你可以把”导入的 HTML”直接当成路由的返回值。Bun.serveroutes 选项接受一个路径到处理器(或 HTML 模块)的映射:

import homepage from "./index.html";

Bun.serve({
  routes: {
    "/": homepage,                       // 直接返回导入的 HTML
    "/api/users": {
      GET() {
        return Response.json([{ id: 1, name: "张三" }]);
      },
      POST(req) {
        return Response.json({ ok: true }, { status: 201 });
      },
    },
  },
  development: true, // 开启 HMR 与 console 回传
});

这里有几个要点:

  • "/" 映射到 homepage(一个被 Bun 当作 HTML 模块导入的文件),访问根路径就返回该页面;
  • "/api/users" 是一个 API 路由,按 HTTP 方法(GET/POST/PUT/DELETE)分发处理函数,返回标准 Response
  • development: true 打开开发体验:前端 HMR、以及把浏览器 console 回传到终端。

20.5.1 动态路由与通配

routes 支持路径参数与通配符:

Bun.serve({
  routes: {
    "/users/:id": (req) => {
      const id = req.params.id; // 从路径取出 :id
      return Response.json({ userId: id });
    },
    "/assets/*": staticHandler, // 通配其余静态资源
  },
});

:id 这类动态段会被解析进 req.params,让你用一份处理器覆盖一类 URL;* 用于兜底匹配。

20.6 import.meta.hot:前端热更新钩子

在浏览器侧代码里,Bun 提供 import.meta.hot 让你精细控制热更新行为。常见用法:

  • import.meta.hot.accept():表示”本模块接受热替换”,更新后不整页刷新;
  • import.meta.hot.accept((mod) => { ... }):拿到新模块并手动应用(例如替换组件状态);
  • import.meta.hot.decline():声明”本模块不能被热替换,必须整页刷新”;
  • import.meta.hot.dispose(() => { ... }):在模块被替换前做清理(如移除事件监听、关闭连接);
  • import.meta.hot.data:在热替换的两次版本间传递状态。
// 一个可热替换的前端模块
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    // 用新模块的逻辑刷新 UI,而不丢失页面状态
    render(newModule.default);
  });

  import.meta.hot.dispose(() => {
    // 旧模块下线前,清理副作用
    cleanup();
  });
}
Warning

import.meta.hot 必须在模块顶层直接调用,不能先赋值给某个变量再延迟调用,否则 Bun 无法在静态分析阶段正确织入 HMR 逻辑。另外,这个 API 只在开发服务器下有意义,生产构建会被自动剔除。

20.7 开发流 vs 生产流

Bun 的 HTML/全栈工作流可以很清晰地分成两段:

开发期:用 bun --hot ./server.ts(或 bun ./index.html 做纯前端),享受 HMR、动态路由、API 同进程调试。development: true 让前端错误与 console 直接回传到终端,调试体验接近主流前端框架的 dev server。

生产期:用 bun build ./index.html --outdir=dist --minify(纯前端站点),或对全栈应用做 AOT 构建(bun build --target=bun --production),把代码预打包,再以普通 Bun.serve 运行编译后的产物,降低运行时开销。

Tip

轻量项目里,甚至可以跳过单独的”前端构建”步骤:开发用 --hot,上线用 bun build --target=bun --production 直接产出优化后的服务器代码。Bun 的”打包器 + 运行时 + 服务器”一体设计,正是为了减少这类工具链的拼接成本。

20.8 小结

本章我们把打包器接入了真实的 HTML 与全栈场景:

  • bun ./index.html 直接启动开发服务器,HTML 是天然入口;
  • bun build ./index.html --outdir=dist 产出可托管的静态站点,bun build --compile ./index.html 把应用编译成单文件可执行文件;
  • bun --hot 提供全栈热重载,浏览器侧走 HMR;
  • Bun.serve({ routes }) 让”导入的 HTML”成为路由返回值,并与 API 路由(GET/POST/动态 :id、通配 *)共存于同一进程;
  • development: true 开启 HMR 与 console 回传;
  • import.meta.hot 提供 accept/decline/dispose/data 等精细热更新钩子,且生产构建会被自动移除。
Note

全栈模式下,前端页面与后端 API 运行在同一进程、共享同一套模块图,因此你在后端 routes 里改了逻辑、前端组件里改了样式,都会经由 --hot 被同一套热重载机制捕获,无需分别启动前端 dev server 与后端服务。这种”单进程全栈”的简洁性,正是 Bun 把运行时、打包器、服务器打包在一起的设计收益之一。

至此,Bun 打包器篇章(第 16–20 章)全部完成:从 bun build 基础、loader 与资源、插件系统、压缩与独立可执行文件,到 HTML 与全栈开发服务器,我们已经能够用一套统一的工具链,覆盖”开发 → 构建 → 分发 → 全栈”的完整链路。下一章起,我们将转入测试主题(bun test)。