Bun

Fullstack Dev Server

使用 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 匯入即路由

網路始於 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 檔案

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>

會變成像這樣

index.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 並渲染您的應用程式。

src/backend.ts
src/frontend.tsx
public/dashboard.html
src/styles.css
src/app.tsx
src/backend.ts
import dashboard from "../public/dashboard.html";
import { serve } from "bun";

serve({
  routes: {
    "/": dashboard,
  },

  async fetch(req) {
    // ...api requests
    return new Response("hello world");
  },
});
src/frontend.tsx
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 />);
});
public/dashboard.html
<!DOCTYPE html>
<html>
  <head>
    <title>Dashboard</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="../src/frontend.tsx"></script>
  </body>
</html>
src/styles.css
body {
  background-color: red;
}
src/app.tsx
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
  },
});

developmenttrue 時,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
bunfig.toml
[serve.static]
plugins = ["bun-plugin-tailwind"]

這將允許您在 HTML 和 CSS 檔案中使用 TailwindCSS 實用程式類別。您只需要在某處匯入 tailwindcss

index.html
<!doctype html>
<html>
  <head>
    <title>Home</title>
    <link rel="stylesheet" href="tailwindcss" />
  </head>
  <body>
    <!-- the rest of your HTML... -->
  </body>
</html>

或在您的 CSS 中

style.css
@import "tailwindcss";

自訂外掛程式

任何匯出 有效捆綁器外掛程式物件(本質上是一個具有 namesetup 欄位的物件)的 JS 檔案或模組都可以放置在 plugins 陣列中。

bunfig.toml
[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 檔案產生最佳化捆綁包,並提供結果。

  1. <script> 處理

    • 轉譯 <script> 標籤中的 TypeScript、JSX 和 TSX
    • 捆綁匯入的依賴項
    • 為偵錯產生來源地圖
    • Bun.serve() 中的 development 不是 true 時進行最小化
    <script type="module" src="./counter.tsx"></script>
    
  2. <link> 處理

    • 處理 CSS 匯入和 <link> 標籤
    • 串連 CSS 檔案
    • 重寫 url 和資源路徑,以在 URL 中包含內容可定址雜湊
    <link rel="stylesheet" href="./styles.css" />
    
  3. <img> & 資源處理

    • 資源連結被重寫為在 URL 中包含內容可定址雜湊
    • CSS 檔案中的小型資源會內嵌到 data: URL 中,減少透過網路傳送的 HTTP 請求總數。
  4. 重寫 HTML

    • 將所有 <script> 標籤合併為單個 <script> 標籤,並在 URL 中包含內容可定址雜湊
    • 將所有 <link> 標籤合併為單個 <link> 標籤,並在 URL 中包含內容可定址雜湊
    • 輸出新的 HTML 檔案
  5. 伺服

    • 來自捆綁器的所有輸出檔案都公開為靜態路由,內部機制與您將 Response 物件傳遞給 Bun.serve() 中的 static 時相同。

這與 Bun.build 處理 HTML 檔案 的方式類似。

開發中

  • 目前尚不支援 bun build。未來將會支援。