前端开发与 HTML Imports
用 Bun 的全栈开发服务器直接运行 HTML、TypeScript 与 CSS,并正确产出生产构建
最后更新于
什么时候用,什么时候不用
| 场景 | 建议 |
|---|---|
| 新项目原型、内部工具、小型 SPA | 直接用 Bun 的 HTML imports,零配置 |
| 想把 API 和前端放进同一个进程 | Bun.serve 的 routes 里挂 .html |
| 已有 Vite/框架项目、依赖其插件生态 | 继续用原工具链,见 生态选型决策 |
| 需要 SSR、流式渲染、服务端组件 | 使用框架(Next、TanStack Start 等),不用本页形态 |
直接运行 HTML
bun ./index.htmlBun 会解析 HTML 并自动打包其中引用的脚本、样式和静态资源;TypeScript 与 JSX 默认可用,读取 tsconfig.json。
- SPA 模式:单个
.html会成为所有路由的回退,客户端路由(如/users/123)正常工作。 - MPA 模式:传入多个文件或 glob(
./**/*.html),Bun 按公共前缀为每个文件建路由。
开发时启用热重载(React 项目即 Fast Refresh):
bun --hot ./index.html--console 可以把浏览器 console 转发到终端;Chrome DevTools 的 Automatic Workspace Folders 支持从浏览器直接保存改动。
与 API 同进程
import index from './index.html';
Bun.serve({
routes: {
'/': index,
'/api/health': () => Response.json({ ok: true }),
},
development: true, // 开发模式:HMR 与更详细的错误
});插件与环境变量
bun add -d bun-plugin-tailwind[serve.static]
plugins = ["bun-plugin-tailwind"]
env = "PUBLIC_*"env = "PUBLIC_*" 只会把匹配前缀的环境变量内联进浏览器产物——这是边界,不是形式:任何不带 PUBLIC_ 前缀的变量都不该出现在前端 bundle 里,服务端密钥更不能为了"方便"改前缀。
生产构建
bun build ./index.html --minify --outdir=dist --env=PUBLIC_*[serve.static] 的 env 只作用于开发服务器;生产构建必须显式传 --env=PUBLIC_*(或 Bun.build 的 env 选项),否则 process.env.PUBLIC_* 不会被替换。两个细节:替换只识别字面量 process.env.FOO 写法;未设置的变量会原样保留,浏览器里可能抛 ReferenceError: process is not defined——构建后在产物里 grep 一遍 process.env 确认替换完整。
注意:插件只能通过 bunfig.toml(开发服务器)或 Bun.build API 使用,bun build CLI 不支持插件参数。构建产物是纯静态文件,交给 CDN 或任意静态托管;--compile --target=browser 可进一步打出单个自包含 HTML。
SPA 回退只是开发服务器行为
bun ./index.html 的全路由回退不会随静态产物带走。生产托管必须自己配置重写规则(未命中路径返回 /index.html),否则深链接刷新返回 404。每家 CDN/静态托管的写法不同,按平台文档配置并用深链接实测。
边界
- 这套能力替代的是"esbuild + 静态服务器 + 手动 HMR"的组合,不是 Vite 生态;迁移前清点你实际用到的 Vite 插件。
- 开发服务器和生产构建共享打包器,但代理、缓存头和压缩仍是你的部署层职责。
- 浏览器产物不得包含服务端密钥;用
PUBLIC_前缀纪律加 code review 双重约束。
验收
--hot下改 React 组件,页面状态保留地刷新;bun build产物在无 Bun 环境的静态服务器上完整可用;- 产物里 grep 不到任何非
PUBLIC_前缀的服务端变量,也没有残留的process.env; - 托管层的重写规则生效:深链接刷新返回
index.html而不是 404。
官方参考:HTML imports 与前端开发服务器、全栈开发、打包器。