【Bun 全面教學】#03 進階功能:內建測試、打包與 HTTP 伺服器

測驗:Bun 進階功能

共 5 題,點選答案後會立即顯示結果

1. 在 Bun 中要使用內建測試框架,應該從哪裡引入 testexpect

  • A. import { test, expect } from "jest"
  • B. import { test, expect } from "@bun/test"
  • C. import { test, expect } from "bun:test"
  • D. import { test, expect } from "vitest"

2. 以下是一個 Bun HTTP 伺服器的程式碼,其中 fetch 函式的作用是什麼?

Bun.serve({ port: 3000, fetch(request) { return new Response(“Hello!”); }, });
  • A. 從遠端 API 取得資料
  • B. 每當收到 HTTP 請求時執行,並回傳回應
  • C. 在伺服器啟動時執行一次初始化
  • D. 定期輪詢檢查伺服器狀態

3. 使用 bun build 打包時,如果目標是在瀏覽器中執行,應該使用哪個參數?

  • A. --target node
  • B. --env browser
  • C. --platform web
  • D. --target browser

4. 關於 Bun 的檔案操作 API,以下敘述何者正確?

const file = Bun.file(“./data.txt”); const content = await file.text();
  • A. Bun.file() 會立即讀取檔案內容
  • B. file.text() 是同步操作,不需要 await
  • C. Bun.file() 只是取得檔案參照,呼叫 file.text() 才真正讀取
  • D. 必須先用 file.open() 開啟檔案才能讀取

5. 在 Bun 中使用內建的 SQLite 支援,應該如何引入 Database 類別?

  • A. import { Database } from "better-sqlite3"
  • B. import { Database } from "bun:sqlite"
  • C. import sqlite from "bun:db"
  • D. const Database = require("sqlite3")

一句話說明

Bun 不只是套件管理器,它內建了測試框架、打包工具和 HTTP 伺服器,讓你不用安裝一堆額外套件就能完成完整的開發流程。

為什麼要學這些?

當 AI 幫你產生專案時,你可能會看到:

// 測試檔案裡
import { expect, test } from "bun:test";

// 伺服器程式裡
Bun.serve({ port: 3000, fetch(req) { ... } });

// 打包指令
// bun build ./src/index.ts --outdir ./dist
Code language: JavaScript (javascript)

這篇教你看懂這些 Bun 特有的寫法,以及它們跟你熟悉的工具有什麼不同。


一、bun test:內建測試框架

最小範例

// math.test.ts
import { expect, test } from "bun:test";

test("1 加 1 等於 2", () => {
  expect(1 + 1).toBe(2);
});
Code language: JavaScript (javascript)

執行測試:

bun test

逐行翻譯

import { expect, test } from "bun:test";
// ↑ 從 Bun 內建的測試模組引入 expect 和 test
//   注意是 "bun:test",不是安裝的套件

test("1 加 1 等於 2", () => {
// ↑ 定義一個測試案例,第一個參數是描述文字

  expect(1 + 1).toBe(2);
  // ↑ 斷言:預期 1+1 的結果要等於 2
});
Code language: JavaScript (javascript)

常見變化

1. 群組測試(describe)

import { expect, test, describe } from "bun:test";

describe("數學運算", () => {
  // ↑ 把相關測試群組在一起

  test("加法", () => {
    expect(2 + 3).toBe(5);
  });

  test("減法", () => {
    expect(5 - 3).toBe(2);
  });
});
Code language: PHP (php)

2. 前置/後置處理

import { expect, test, beforeEach, afterEach } from "bun:test";

let counter = 0;

beforeEach(() => {
  // ↑ 每個測試執行「之前」會跑這段
  counter = 0;
});

afterEach(() => {
  // ↑ 每個測試執行「之後」會跑這段
  console.log("測試完成");
});

test("計數器從 0 開始", () => {
  expect(counter).toBe(0);
});
Code language: JavaScript (javascript)

3. 非同步測試

import { expect, test } from "bun:test";

test("非同步操作", async () => {
  // ↑ 加上 async,裡面就能用 await

  const response = await fetch("https://api.example.com/data");
  expect(response.ok).toBe(true);
});
Code language: JavaScript (javascript)

與 Jest 的對照

Jest 寫法 Bun 寫法 差異
import { test } from '@jest/globals' import { test } from "bun:test" 來源不同
jest.fn() mock() Mock 函式名稱不同
jest.spyOn() spyOn() 直接引入使用

大部分 Jest 語法都能直接用,只有 import 來源和部分 mock 相關 API 不同。

Vibe Coder 檢查點

看到 bun:test 時要確認:

  • [ ] 測試檔案命名是否符合規則(.test.ts.spec.ts
  • [ ] expect 的斷言邏輯是否正確
  • [ ] 非同步測試有沒有加 async/await

二、bun build:打包工具

最小範例

bun build ./src/index.ts --outdir ./dist

這會把 src/index.ts(包含它引用的所有模組)打包成一個檔案,輸出到 dist 資料夾。

常見指令解釋

bun build ./src/index.ts --outdir ./dist
#         ↑ 入口檔案        ↑ 輸出目錄

bun build ./src/index.ts --outfile ./bundle.js
#                        ↑ 指定輸出的單一檔案名稱

bun build ./src/index.ts --minify
#                        ↑ 壓縮程式碼(移除空白、縮短變數名)

bun build ./src/index.ts --target browser
#                        ↑ 目標環境:browser(瀏覽器)或 bun(伺服器)

bun build ./src/index.ts --splitting
#                        ↑ 啟用 code splitting(拆分共用模組)
Code language: PHP (php)

用程式呼叫打包

// build.ts
const result = await Bun.build({
  entrypoints: ["./src/index.ts"],
  // ↑ 入口檔案,可以有多個

  outdir: "./dist",
  // ↑ 輸出目錄

  minify: true,
  // ↑ 是否壓縮

  target: "browser",
  // ↑ 目標環境
});

if (!result.success) {
  console.error("打包失敗:", result.logs);
}
Code language: JavaScript (javascript)

與 Webpack/Vite 的對照

功能 Webpack Vite Bun
設定檔 webpack.config.js(複雜) vite.config.ts 不需要或很簡單
TypeScript 需要 ts-loader 內建 內建
速度 較慢 非常快
Tree Shaking 需設定 內建 內建

Vibe Coder 檢查點

看到 bun build 時要確認:

  • [ ] 入口檔案路徑是否正確
  • [ ] 輸出目錄是否符合專案結構
  • [ ] target 是 browser 還是 bun(影響可用的 API)

三、Bun.serve():HTTP 伺服器

最小範例

// server.ts
Bun.serve({
  port: 3000,
  fetch(request) {
    return new Response("Hello!");
  },
});

console.log("伺服器啟動於 http://localhost:3000");
Code language: JavaScript (javascript)

執行:

bun run server.ts
Code language: CSS (css)

逐行翻譯

Bun.serve({
  // ↑ 啟動一個 HTTP 伺服器

  port: 3000,
  // ↑ 監聽 3000 埠

  fetch(request) {
    // ↑ 每當收到請求時,執行這個函式
    //   request 包含請求的所有資訊

    return new Response("Hello!");
    // ↑ 回傳一個 Response 物件給客戶端
  },
});
Code language: JavaScript (javascript)

常見變化

1. 處理不同路由

Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    // ↑ 解析請求的 URL

    if (url.pathname === "/") {
      return new Response("首頁");
    }

    if (url.pathname === "/api/users") {
      return Response.json({ users: [] });
      // ↑ 回傳 JSON 格式
    }

    return new Response("Not Found", { status: 404 });
    // ↑ 找不到路由時回傳 404
  },
});
Code language: JavaScript (javascript)

2. 處理 POST 請求

Bun.serve({
  port: 3000,
  async fetch(request) {
    // ↑ 加 async 因為要 await 讀取 body

    if (request.method === "POST") {
      const body = await request.json();
      // ↑ 解析 JSON 格式的請求內容

      console.log("收到資料:", body);
      return Response.json({ received: true });
    }

    return new Response("Send a POST request");
  },
});
Code language: JavaScript (javascript)

3. 提供靜態檔案

Bun.serve({
  port: 3000,
  async fetch(request) {
    const url = new URL(request.url);
    const filePath = `./public${url.pathname}`;
    // ↑ 將 URL 路徑對應到 public 資料夾

    const file = Bun.file(filePath);
    // ↑ Bun.file() 取得檔案參照

    if (await file.exists()) {
      return new Response(file);
      // ↑ 直接回傳檔案
    }

    return new Response("Not Found", { status: 404 });
  },
});
Code language: JavaScript (javascript)

與 Express 的對照

// Express 寫法
import express from "express";
const app = express();

app.get("/", (req, res) => {
  res.send("Hello!");
});

app.listen(3000);
Code language: JavaScript (javascript)
// Bun.serve 寫法
Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === "/" && request.method === "GET") {
      return new Response("Hello!");
    }
    return new Response("Not Found", { status: 404 });
  },
});
Code language: JavaScript (javascript)
特性 Express Bun.serve
路由系統 內建完整路由 需自己判斷或用框架
中介軟體 豐富生態系 需自己實作或用框架
效能 普通 非常高
學習曲線 較平緩 需理解 Web API 標準

Vibe Coder 檢查點

看到 Bun.serve 時要確認:

  • [ ] port 有沒有跟其他服務衝突
  • [ ] fetch 函式有沒有處理所有可能的路由
  • [ ] 錯誤情況有沒有回傳適當的狀態碼

四、其他實用內建 API

Bun.file():檔案操作

// 讀取檔案
const file = Bun.file("./data.txt");
const text = await file.text();
// ↑ 讀取為文字

const json = await Bun.file("./config.json").json();
// ↑ 直接解析為 JSON

// 寫入檔案
await Bun.write("./output.txt", "Hello, Bun!");
// ↑ 寫入文字到檔案

await Bun.write("./data.json", JSON.stringify({ name: "Bun" }));
// ↑ 寫入 JSON
Code language: JavaScript (javascript)

逐行翻譯

const file = Bun.file("./data.txt");
// ↑ 取得檔案的參照(還沒真的讀取)

console.log(file.size);        // 檔案大小(bytes)
console.log(file.type);        // MIME 類型,如 "text/plain"
console.log(await file.exists()); // 檔案是否存在

const content = await file.text();
// ↑ 這時才真的讀取內容
Code language: JavaScript (javascript)

bun:sqlite:內建 SQLite

import { Database } from "bun:sqlite";
// ↑ 從 Bun 內建模組引入

const db = new Database("mydb.sqlite");
// ↑ 開啟(或建立)資料庫檔案

// 建立資料表
db.run(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL
  )
`);

// 插入資料
db.run("INSERT INTO users (name) VALUES (?)", ["Alice"]);
// ↑ ? 是參數佔位符,防止 SQL injection

// 查詢資料
const users = db.query("SELECT * FROM users").all();
// ↑ .all() 取得所有結果
console.log(users); // [{ id: 1, name: "Alice" }]

// 查詢單筆
const user = db.query("SELECT * FROM users WHERE id = ?").get(1);
// ↑ .get() 取得單筆結果
Code language: JavaScript (javascript)

常見 API 快速對照表

需求 Node.js Bun
讀檔案 fs.readFileSync() await Bun.file().text()
寫檔案 fs.writeFileSync() await Bun.write()
HTTP 伺服器 http.createServer() 或 Express Bun.serve()
SQLite 需安裝 better-sqlite3 內建 bun:sqlite
執行測試 需安裝 Jest/Vitest 內建 bun test
打包 需安裝 Webpack/Vite 內建 bun build

五、實戰:用 Bun 建立完整 API

把上面學到的組合起來:

// api.ts
import { Database } from "bun:sqlite";

// 初始化資料庫
const db = new Database("todos.sqlite");
db.run(`
  CREATE TABLE IF NOT EXISTS todos (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    done INTEGER DEFAULT 0
  )
`);

// 啟動伺服器
Bun.serve({
  port: 3000,

  async fetch(request) {
    const url = new URL(request.url);

    // GET /todos - 取得所有待辦事項
    if (url.pathname === "/todos" && request.method === "GET") {
      const todos = db.query("SELECT * FROM todos").all();
      return Response.json(todos);
    }

    // POST /todos - 新增待辦事項
    if (url.pathname === "/todos" && request.method === "POST") {
      const { title } = await request.json();
      db.run("INSERT INTO todos (title) VALUES (?)", [title]);
      return Response.json({ success: true });
    }

    return new Response("Not Found", { status: 404 });
  },
});

console.log("API 伺服器啟動於 http://localhost:3000");
Code language: JavaScript (javascript)

測試 API:

# 新增待辦事項
curl -X POST http://localhost:3000/todos \
  -H "Content-Type: application/json" \
  -d '{"title": "學習 Bun"}'

# 取得所有待辦事項
curl http://localhost:3000/todos
Code language: PHP (php)

六、必看懂 vs 知道就好

必看懂(常常會出現)

  • import { ... } from "bun:test" – 測試相關引入
  • Bun.serve({ fetch() {} }) – HTTP 伺服器基本結構
  • Bun.file() / Bun.write() – 檔案讀寫
  • new Response() / Response.json() – HTTP 回應

知道就好(遇到再查)

  • Bun.spawn() – 執行子程序
  • Bun.Transpiler – 程式碼轉譯 API
  • Bun.password – 密碼雜湊工具
  • Bun.CryptoHasher – 雜湊運算
  • WebSocket 支援的詳細設定

總結

這篇介紹了 Bun 的三大內建功能:

功能 指令/API 取代什麼
測試 bun test Jest, Vitest
打包 bun build Webpack, Vite, esbuild
HTTP 伺服器 Bun.serve() Express, Fastify

加上其他實用 API:

  • Bun.file() / Bun.write() 取代 Node.js 的 fs 模組
  • bun:sqlite 取代 better-sqlite3

Bun 的設計哲學是「All-in-One」,讓你用更少的套件完成更多事情。當 AI 產生的程式碼使用這些 Bun 特有的 API 時,你現在應該能看懂它們在做什麼了。


下一篇預告

下一篇是本系列最後一篇,我們會介紹 Bun 與現有生態系的整合,包括如何在 Bun 專案中使用 React、Next.js 等框架,以及遷移現有專案的注意事項。

進階測驗:Bun 進階功能

測驗目標:驗證你是否能在實際情境中應用所學。
共 5 題,包含情境題與錯誤診斷題。

1. 你正在開發一個待辦事項 API,需要同時使用資料庫和 HTTP 伺服器。以下哪種做法最適合 Bun 專案? 情境題

  • A. 安裝 Express 和 better-sqlite3,用熟悉的方式開發
  • B. 安裝 Fastify 和 sql.js,這些套件效能較好
  • C. 使用 Bun.serve() 搭配 npm 的 sqlite3 套件
  • D. 使用 Bun.serve() 搭配 bun:sqlite,完全使用內建功能

2. 你需要為前端 React 專案打包 TypeScript 程式碼,並且希望輸出的檔案盡可能小。應該使用哪個指令? 情境題

  • A. bun build ./src/index.ts --outdir ./dist
  • B. bun build ./src/index.ts --target bun --minify
  • C. bun build ./src/index.ts --target browser --minify --outdir ./dist
  • D. bun build ./src/index.ts --splitting --outdir ./dist

3. 你的 HTTP 伺服器需要根據 URL 路徑回傳不同內容:首頁回傳 HTML,/api/data 回傳 JSON,其他路徑回傳 404。以下哪個實作最正確? 情境題

Bun.serve({ port: 3000, fetch(request) { const url = new URL(request.url); // 這裡要填入什麼? }, });
  • A. 使用 request.pathname 判斷路由
  • B. 使用 url.pathname 搭配 if 判斷,用 Response.json() 回傳 JSON
  • C. 使用 app.get("/")app.get("/api/data") 定義路由
  • D. 使用 url.path 判斷,用 JSON.stringify() 回傳 JSON

4. 小明寫了一個測試但執行 bun test 後沒有任何輸出,測試似乎沒有被執行。請問最可能的原因是什麼? 錯誤診斷

// calculator.ts import { expect, test } from “bun:test”; test(“加法測試”, () => { expect(2 + 2).toBe(4); });
  • A. expecttest 應該從 “jest” 引入
  • B. 測試函式應該使用 async 關鍵字
  • C. 檔案名稱應該是 *.test.ts*.spec.ts,而非 calculator.ts
  • D. 需要先執行 bun install 安裝測試依賴

5. 以下程式碼嘗試讀取 JSON 檔案,但執行時發生錯誤。請問問題出在哪裡? 錯誤診斷

const file = Bun.file(“./config.json”); const config = file.json(); console.log(config.name);
  • A. 應該使用 Bun.read("./config.json") 讀取檔案
  • B. file.json() 是非同步操作,需要加上 await
  • C. 應該先呼叫 file.open() 才能使用 json()
  • D. Bun.file() 不支援 JSON 檔案,只能讀取純文字

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *