使用 Bun.serve()
的 routes
选项,您可以在同一个应用中运行前端和后端,无需额外步骤。
要开始使用,导入 HTML 文件并将它们传递给 Bun.serve()
中的 routes
选项。
import { sql, serve } from "bun";
import dashboard from "./dashboard.html";
import homepage from "./index.html";
const server = serve({
routes: {
// ** HTML imports **
// Bundle & route index.html to "/". This uses HTMLRewriter to scan the HTML for `<script>` and `<link>` tags, run's Bun's JavaScript & CSS bundler on them, transpiles any TypeScript, JSX, and TSX, downlevels CSS with Bun's CSS parser and serves the result.
"/": homepage,
// Bundle & route dashboard.html to "/dashboard"
"/dashboard": dashboard,
// ** API endpoints ** (Bun v1.2.3+ required)
"/api/users": {
async GET(req) {
const users = await sql`SELECT * FROM users`;
return Response.json(users);
},
async POST(req) {
const { name, email } = await req.json();
const [user] =
await sql`INSERT INTO users (name, email) VALUES (${name}, ${email})`;
return Response.json(user);
},
},
"/api/users/:id": async req => {
const { id } = req.params;
const [user] = await sql`SELECT * FROM users WHERE id = ${id}`;
return Response.json(user);
},
},
// Enable development mode for:
// - Detailed error messages
// - Hot reloading (Bun v1.2.3+ required)
development: true,
// Prior to v1.2.3, the `fetch` option was used to handle all API requests. It is now optional.
// async fetch(req) {
// // Return 404 for unmatched routes
// return new Response("Not Found", { status: 404 });
// },
});
console.log(`Listening on ${server.url}`);
bun run app.ts
HTML 导入即路由
Web 应用始于 HTML,Bun 的全栈开发服务器也是如此。
要指定前端的入口点,请将 HTML 文件导入到您的 JavaScript/TypeScript/TSX/JSX 文件中。
import dashboard from "./dashboard.html";
import homepage from "./index.html";
这些 HTML 文件在 Bun 的开发服务器中用作路由,您可以将它们传递给 Bun.serve()
。
Bun.serve({
routes: {
"/": homepage,
"/dashboard": dashboard,
}
fetch(req) {
// ... api requests
},
});
当您向 /dashboard
或 /
发出请求时,Bun 会自动捆绑 HTML 文件中的 <script>
和 <link>
标签,将它们公开为静态路由,并提供结果。
像这样的 index.html 文件
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href="./reset.css" />
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="./sentry-and-preloads.ts"></script>
<script type="module" src="./my-app.tsx"></script>
</body>
</html>
变成这样
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href="/index-[hash].css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/index-[hash].js"></script>
</body>
</html>
如何与 React 一起使用
要在客户端代码中使用 React,请导入 react-dom/client
并渲染您的应用。
import dashboard from "../public/dashboard.html";
import { serve } from "bun";
serve({
routes: {
"/": dashboard,
},
async fetch(req) {
// ...api requests
return new Response("hello world");
},
});
import "./styles.css";
import { createRoot } from "react-dom/client";
import { App } from "./app.tsx";
document.addEventListener("DOMContentLoaded", () => {
const root = createRoot(document.getElementById("root"));
root.render(<App />);
});
<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="../src/frontend.tsx"></script>
</body>
</html>
body {
background-color: red;
}
export function App() {
return <div>Hello World</div>;
}
开发模式
在本地构建时,通过在 Bun.serve()
中设置 development: true
来启用开发模式。
import homepage from "./index.html";
import dashboard from "./dashboard.html";
Bun.serve({
routes: {
"/": homepage,
"/dashboard": dashboard,
}
development: true,
fetch(req) {
// ... api requests
},
});
当 development
为 true
时,Bun 将会
- 在响应中包含
SourceMap
标头,以便开发工具可以显示原始源代码 - 禁用代码压缩
- 在每次请求 .html 文件时重新打包资源
生产模式
在生产环境中部署您的应用程序时,在 Bun.serve()
中设置 development: false
。
- 启用捆绑资源的内存缓存。Bun 将在首次请求
.html
文件时延迟捆绑资源,并将结果缓存在内存中,直到服务器重启。 - 启用
Cache-Control
标头和ETag
标头 - 压缩 JavaScript/TypeScript/TSX/JSX 文件
插件
Bun 的 打包器插件 在捆绑静态路由时也受支持。
要为 Bun.serve
配置插件,请在您的 bunfig.toml
的 [serve.static]
部分添加一个 plugins
数组。
在 HTML 路由中使用 TailwindCSS
例如,通过安装并添加 bun-plugin-tailwind
插件,在您的路由上启用 TailwindCSS
bun add bun-plugin-tailwind
[serve.static]
plugins = ["bun-plugin-tailwind"]
这将允许您在 HTML 和 CSS 文件中使用 TailwindCSS 实用程序类。您只需在某处导入 tailwindcss
即可
<!doctype html>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href="tailwindcss" />
</head>
<body>
<!-- the rest of your HTML... -->
</body>
</html>
或者在您的 CSS 中
@import "tailwindcss";
自定义插件
任何导出一个有效的打包器插件对象(本质上是一个具有 name
和 setup
字段的对象)的 JS 文件或模块都可以放置在 plugins
数组中
[serve.static]
plugins = ["./my-plugin-implementation.ts"]
Bun 将延迟解析并加载每个插件,并使用它们来捆绑您的路由。
注意:这目前位于 bunfig.toml
中,以便在最终将其与 bun build
CLI 集成时,可以静态地知道正在使用哪些插件。这些插件在 Bun.build()
的 JS API 中工作,但尚不支持在 CLI 中使用。
工作原理
Bun 使用 HTMLRewriter
扫描 HTML 文件中的 <script>
和 <link>
标签,将它们用作 Bun 打包器的入口点,为 JavaScript/TypeScript/TSX/JSX 和 CSS 文件生成优化的捆绑包,并提供结果。
<script>
处理- 转译
<script>
标签中的 TypeScript、JSX 和 TSX - 捆绑导入的依赖项
- 生成用于调试的 sourcemap
- 当
Bun.serve()
中的development
不为true
时进行压缩
<script type="module" src="./counter.tsx"></script>
- 转译
<link>
处理- 处理 CSS 导入和
<link>
标签 - 连接 CSS 文件
- 重写
url
和资源路径,以在 URL 中包含内容可寻址哈希值
<link rel="stylesheet" href="./styles.css" />
- 处理 CSS 导入和
<img>
和资源处理- 指向资源的链接被重写为在 URL 中包含内容可寻址哈希值
- CSS 文件中的小资源被内联到
data:
URL 中,从而减少通过网络发送的 HTTP 请求总数
重写 HTML
- 将所有
<script>
标签合并为单个<script>
标签,并在 URL 中包含内容可寻址哈希值 - 将所有
<link>
标签合并为单个<link>
标签,并在 URL 中包含内容可寻址哈希值 - 输出新的 HTML 文件
- 将所有
服务
- 来自打包器的所有输出文件都作为静态路由公开,内部使用与您将
Response
对象传递给Bun.serve()
中的static
时相同的机制。
- 来自打包器的所有输出文件都作为静态路由公开,内部使用与您将
这与 Bun.build
处理 HTML 文件 的方式类似。
这仍在开发中
- 这还不支持
bun build
。未来会支持。