首先,导入 HTML 文件并将它们作为 routes 选项传递给 Bun.serve()。
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.tsHTML 导入即路由
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 文件时重新捆绑资产
- 启用热模块重新加载(除非设置了
hmr: false)
将浏览器控制台日志回显到终端
Bun.serve() 支持将浏览器控制台日志回显到终端。
要启用此功能,请在 Bun.serve() 的 development 对象中传递 console: true。
import homepage from "./index.html";
Bun.serve({
// development can also be an object.
development: {
// Enable Hot Module Reloading
hmr: true,
// Echo console logs from the browser to the terminal
console: true,
},
routes: {
"/": homepage,
},
});
当设置 console: true 时,Bun 会将浏览器中的控制台日志流式传输到终端。这会重用 HMR 的现有 WebSocket 连接来发送日志。
生产模式
热重载和 development: true 有助于您快速迭代,但在生产环境中,您的服务器应该尽可能快,并且尽可能少地依赖外部依赖项。
提前捆绑(推荐)
从 Bun v1.2.17 开始,您可以使用 Bun.build 或 bun build 提前捆绑您的全栈应用程序。
bun build --target=bun --production --outdir=dist ./src/index.ts当 Bun 的捆绑器从服务器端代码中看到 HTML 导入时,它会将引用的 JavaScript/TypeScript/TSX/JSX 和 CSS 文件捆绑到一个清单对象中,Bun.serve() 可以使用该对象来提供资产。
import { serve } from "bun";
import index from "./index.html";
serve({
routes: { "/": index },
});
在内部,index 变量是一个清单对象,看起来像这样
运行时捆绑
当添加构建步骤过于复杂时,您可以在 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>标签 - 将所有
<link>标签组合成一个带有内容寻址哈希的<link>标签 - 输出新的 HTML 文件
- 将所有
服务
- 所有来自捆绑器的输出文件都作为静态路由公开,内部机制与将
Response对象传递给Bun.serve()中的static相同。
- 所有来自捆绑器的输出文件都作为静态路由公开,内部机制与将
这与 Bun.build 处理 HTML 文件的方式类似。
这是一个正在进行中的工作
- 这还不支持
bun build。未来也会支持。