编程 Bun 1.3 深度拆解:当全栈工具包决定让 Claude 把自己从 Zig 重写进 Rust——从 JavaScriptCore 内核、统一数据库 API 到 AI 驱动百万行重写的新范式

2026-08-12 05:43:21 +0800 CST views 4

Bun 1.3 深度拆解:当全栈工具包决定让 Claude 把自己从 Zig 重写进 Rust——从 JavaScriptCore 内核、统一数据库 API 到 AI 驱动百万行重写的新范式

如果一个 JavaScript 运行时,既能当 Node 用、又能当 Webpack 用、还能当 Jest 用、甚至把 PostgreSQL 客户端和 S3 SDK 都内置了,你会不会觉得它"管得太宽"?
2026 年的 Bun 就是这样一位"什么都想干"的选手。更离谱的是:它的创始人 Jarred Sumner 在 5 月 11 日发了一条推文,宣布要用 Claude Code 把整个运行时从 Zig 重写进 Rust,六天改写近百万行代码;与此同时,Bun 宣布加入 Anthropic
这篇文章,我们把它扒个底朝天。


一、背景:JavaScript 运行时的三次浪潮

要理解 Bun 为什么存在,得先看懂 JavaScript 运行时这三十年来的三次"范式革命"。

第一次浪潮:Node.js(2009)。Ryan Dahl 用 V8 + libuv 把 JavaScript 带到了服务端,用事件循环和"非阻塞 IO"重新定义了后端。这是一次"能不能跑"的革命。但它留下了两个历史包袱:

  1. CommonJS 模块系统——当年为了快,设计得相当随意,导致今天的 ESM / CJS 互操作仍是一团浆糊;
  2. npm 的"依赖地狱"——node_modules 体积爆炸、锁文件玄学、安装动辄几十秒。

第二次浪潮:Deno(2018)。还是 Ryan Dahl,带着"我后悔了"的反思卷土重来:默认安全沙箱、原生支持 TypeScript、URL import、内置工具链。这是一次"能不能更安全、更现代"的革命。但 Deno 的 URL import 和过于理想化的设计,让它在工程落地上始终不温不火。

第三次浪潮:Bun(2022)。前 Stripe 工程师 Jarred Sumner 换了个角度思考——与其重新设计语言生态,不如把开发者每天要用的所有工具都重新造一遍,并且让它快得离谱。Bun 的口号从一开始就不是"更好的 Node",而是 "All-in-One Toolkit":一个二进制文件,同时是 runtime、bundler、transpiler、package manager、test runner、task runner。

到 2026 年 8 月,Bun 已经迭代到 v1.3.14。这一年的两个新闻直接把 Bun 推上了风口:

  • Bun 加入 Anthropic:官方站点头条直接挂着 "Bun is joining Anthropic",Anthropic 押注 Bun 作为 AI 原生时代的 JS/TS 工具链底座。
  • Zig → Rust 重写:创始人宣布用 AI 把底层从 Zig 迁到 Rust,近百万行代码、六天完成,通过了 99.8% 的既有测试套件。

这两个事件叠加在一起,让 Bun 的故事从"又一个快一点的运行时",变成了一个关于工程可靠性、AI 辅助重写、以及工具链整合边界的深层样本。下面我们一层层拆。


二、核心概念:到底什么是 "All-in-One Toolkit"

很多初学者对 Bun 的误解是"哦,又一个 Node 替代品"。错。Bun 的野心是把下面这些东西全部塞进一个二进制

能力Bun 提供传统 Node 生态要装什么
运行时bun runnode
包管理bun installnpm / pnpm / yarn
打包器bun buildwebpack / rollup / esbuild
转译器内置(TS/JSX/CSS)babel / tsc / swc
测试bun testjest / vitest
任务运行bun run <script>npm scripts
SQLite 客户端bun:sqlitebetter-sqlite3
SQL 客户端bun:sql(PG/MySQL/SQLite)pg / mysql2
对象存储Bun.s3aws-sdk / @aws-sdk/client-s3
密钥管理Bun.secretsdotenv + Vault
FFIbun:ffiffi-napi / node-ffi

注意一个关键点:Bun 不是把 npm 包拼起来,而是用 Zig(现在是 Rust)从底层重新实现这些能力。这意味着它的 SQLite 驱动不是对 better-sqlite3 的封装,而是直接用原生代码绑定;它的 HTTP 服务不是基于 Node 的 http 模块,而是自建的事件循环。

2.1 引擎之争:为什么是 JavaScriptCore 而不是 V8

Bun 选择 JavaScriptCore(JSC,Safari 的引擎) 而非 Node 用的 V8。这是个反直觉但经过计算的决定:

  • 冷启动更快:JSC 的字节码缓存和启动路径比 V8 轻,对 CLI、Serverless、短生命周期进程尤其友好;
  • 内存占用更低:同样的 "Hello World" 服务,JSC 常驻内存往往显著低于 V8;
  • 更易嵌入:JSC 的 C API 比 V8 的嵌入接口友好,这让用 Zig/Rust "手搓" 绑定变得可行。

代价是:V8 在长时间运行、重计算场景(比如大型 SSR)的峰值性能与 JIT 成熟度更强。所以 Bun 快,但"快在哪"需要分场景看——这不是简单的"谁更快"。

2.2 Node.js 兼容性的"阴招":直接跑 Node 的测试套件

Bun 有一个让开发者又爱又恨的兼容策略:它主动把 Node.js 官方的测试套件拉过来自己跑,逐模块提升通过率。这意味着 httpcryptodgramfs 等核心模块的 Node 兼容测试通过率已经被推到 90%+ 以上。

它的兼容性哲学是:不追求 100% 字节级兼容,但追求"把你真实项目跑起来"。比如 Express 在 Bun 上能直接跑,而且官方基准里 Express 在 Bun 上的吞吐比在 Node 上快约 3 倍——因为 Bun 的 http 底层是基于高性能网络栈重写的,而不是 Node 那套。

2.3 bun: 命名空间:Bun 的"私房 API"

Bun 把原生能力挂在 bun: 前缀的模块下,避免和 Web 标准 / Node API 冲突:

  • bun:sqlite —— 同步、零拷贝的 SQLite 客户端;
  • bun:ffi —— 零配置调用 C 动态库;
  • bun:test —— 测试框架;
  • bun:html —— 把 HTML 转成 JSX(用于框架集成);
  • bun:jsc —— 直接操控 JavaScriptCore 内部(内存、垃圾回收洞察)。

而 1.3 引入的 bun:sql 是本文重点,下一节细说。


三、架构分析:Bun 为什么快,以及那场震惊社区的 Rust 重写

3.1 快的三层地基

Bun 的性能不是"调出来的",而是从架构地基上长出来的:

  1. 引擎层:JavaScriptCore 的轻量启动 + 自带字节码缓存;
  2. 系统层:底层用 Zig(现迁移 Rust)手写,配合 mimalloc 作为全局内存分配器,减少 malloc 碎片;文件 IO、DNS、HTTP 都走自建的高性能实现,而不是 Node 的 libuv 路径;
  3. 工具链层:转译器和打包器用自家 parser(基于 zig 的 parser,正在迁 Rust),能在内存里直接完成 TS/JSX → JS,没有 babel 那种多进程插件链的开销。

官方基准(注意这是 Bun 自家数据,需自己复现验证):

  • 冷启动:bun -e '...'node -e '...' 快约 4 倍;
  • 包安装:bun installnpm install 快约 25–30 倍;
  • 打包:bun build 在多数场景快于 esbuild / webpack。

3.2 Bun 1.3 的"全栈化"三连击

1.3 被称为 "Bun 史上最大版本",核心是把 Bun 从"高性能运行时"升级成"一站式全栈方案":

  • 统一数据库 API bun:sql:一套 API 同时支持 PostgreSQL、MySQL/MariaDB、SQLite,靠连接串切换,SQL 与参数化语法一致;
  • 零配置前端开发:内置 HMR(热重载),底层用各平台原生文件监听(macOS 的 kqueue、Linux 的 inotify、Windows 的 ReadDirectoryChangesW),比 JS 实现的文件监听快 10 倍以上;支持 React Fast Refresh,通过 import.meta.hot 自定义热更新;
  • 内置 Redis 客户端 + 既有 S3 客户端,把"后端要连的服务"也一并收编。

3.3 那场 Rust 重写:为什么,以及怎么做到的

这是 2026 年最炸裂的工程事件之一。Jarred Sumner 在 5 月 11 日的推文原文大意是:

"Bun v1.3.14 明天发布。如果我们合并 Rust 重写版本,那这将是 Zig 的最后一个版本。"

为什么要从 Zig 迁到 Rust? 核心原因是可靠性。Sumner 公开表示:Zig 经常出现内存错误(memory bug)和崩溃,而且很难彻底修复;Rust 的所有权系统和借用检查器能在编译期就挡住 dangling pointer、buffer overflow、数据竞争这类问题。对一个每天处理数十亿次请求的 JS 运行时来说,这种"编译期保证"的价值难以估量。

怎么做到的? 几个关键事实:

  • 这次重写大量借助 Claude Code 完成,涉及约 96 万到 100 万行代码
  • 从 "代码跑不起来" 到 "通过既有测试套件 99.8%(Linux x64 glibc)",大约只花了 六天
  • 迁移是增量式的:先以 draft batch 形式合入底层模块(如 ConcurrentTask、全局分配器调优、source map 热路径用 SmallVec 栈上暂存),逐步替换,而不是一次性推倒重来;
  • GitHub 上能看到对应的合并 PR(如 Rewrite Bun in Rust),perf 相关 commit 显示团队在迁移的同时还在抠性能细节(mimalloc 的 MI_NO_SET_VMA_NAME 跳过 prctl、bundler worker 的 arena 复用等)。

这意味着什么? 短期看,v1.3.14 可能成为"最后一个纯 Zig 构建";长期看,Bun 的可靠性基线会被 Rust 抬高,而 AI 辅助大规模重写也第一次在"生产级基础设施"级别被验证。

但——争议也随之而来

3.4 "你管得太宽了":整合边界的质疑

Bun 越做越大的同时,社区出现了两种尖锐声音:

  • "一个工具想干完所有事,是不是太多了?" 当 Bun 开始内置数据库客户端、S3、Redis、密钥管理,开发者担心它变成"无法audit 的黑盒",也担心某个内置模块质量跟不上专业库(比如 pg / aws-sdk 多年沉淀的边界处理)。
  • 真实的反噬案例:有知名开源项目(如 yt-dlp)以安全为由弃用了低版本 Bun;部分子项目(如 Electrobun)选择解绑。这给 Bun 的统一叙事泼了冷水——整合带来的便利,和专业化带来的信任,是此消彼长的

我的判断:Bun 的"全栈化"对个人开发者、创业团队、Demo 与原型是巨大福音;但对强合规、重审计、已有成熟基建的大厂,它会先被当成"实验性底座",而非默认选择。这很合理。


四、代码实战:一个 Bun 全栈工程的完整切片

光说架构太虚,下面用真实可跑的切片,把 Bun 的核心能力串起来。

4.1 工程骨架与脚本

{
  "name": "bun-fullstack-demo",
  "module": "src/index.ts",
  "type": "module",
  "scripts": {
    "dev": "bun run --hot src/index.ts",
    "build": "bun build ./public/index.html --outdir=./dist --minify",
    "test": "bun test",
    "start": "bun run src/index.ts"
  },
  "devDependencies": {
    "bun-types": "latest"
  }
}

bun install 会生成文本格式bun.lock(注意:早期是二进制 bun.lockb,1.2 起切换为文本锁文件,便于 code review 和冲突解决,且安装速度反而更快)。

4.2 HTTP 服务 + WebSocket(一个二进制搞定)

// src/index.ts
Bun.serve({
  port: 3000,

  // 普通 HTTP 请求
  fetch(req, server) {
    const url = new URL(req.url);

    if (url.pathname === "/api/health") {
      return Response.json({
        ok: true,
        runtime: "bun",
        version: Bun.version,
        uptime: process.uptime(),
      });
    }

    if (url.pathname === "/api/echo") {
      const body = await req.json().catch(() => ({}));
      return Response.json({ youSent: body });
    }

    // 把连接升级为 WebSocket
    if (url.pathname === "/ws") {
      const upgraded = server.upgrade(req);
      if (upgraded) return undefined; // 已被 WebSocket 接管
      return new Response("upgrade failed", { status: 500 });
    }

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

  // WebSocket 生命周期
  websocket: {
    open(ws) {
      ws.send("connected");
    },
    message(ws, data) {
      ws.send(`echo @ ${new Date().toISOString()}: ${data}`);
    },
    close(ws) {
      console.log("client left");
    },
  },
});

console.log("🚀 listening on http://localhost:3000");

注意这里的 fetch(req, server) 双参数签名:第二个 server 专门用于 server.upgrade() 做 WebSocket 升级。这是 Bun 相比"裸 Node http"更顺手的地方之一。

4.3 本地存储:bun:sqlite 同步 API

import { Database } from "bun:sqlite";

const db = new Database("app.db", { create: true });

db.run(`
  CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    views INTEGER NOT NULL DEFAULT 0,
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
  )
`);

// 参数化插入(? 占位,杜绝 SQL 注入)
const insert = db.query(
  "INSERT INTO posts (title, views) VALUES (?, ?)"
);
insert.run("深入理解 Bun 1.3", 0);

// 查询
const recent = db.query(
  "SELECT * FROM posts ORDER BY id DESC LIMIT ?"
);
console.log(recent.all(10));

// 事务批量写入
const insertMany = db.query("INSERT INTO posts (title) VALUES ($title)");
const tx = db.transaction((titles: string[]) => {
  for (const t of titles) insertMany.run({ $title: t });
});
tx(["文章A", "文章B", "文章C"]);

bun:sqlite同步的(基于 SQLite 原生同步 API + Bun 的事件循环友好调度),写起来比 better-sqlite3 还省心,且零原生编译烦恼。

4.4 统一数据库 API:bun:sql(PG / MySQL / SQLite 一套搞定)

这是 1.3 的王炸。同样一套 API,换连接串即可切换数据库:

import { sql, SQL } from "bun";

// 统一 API:差异仅在连接串
const pg = new SQL("postgres://user:pass@localhost:5432/app");
const mysql = new SQL("mysql://user:pass@localhost:3306/app");
const sqlite = new SQL("sqlite://data.db");

const username = "alice";

// 标签模板 + 自动参数化,从根本上防注入
const rows = await sql`
  SELECT id, name, role
  FROM users
  WHERE username = ${username}
    AND active = TRUE
`;

console.log(rows);
await pg.close();

对全栈开发者而言,这意味着:业务代码不需要因为"今天用 PG、明天换 MySQL"而重写。迁移成本被压到了一行连接串。

4.5 对象存储:Bun.s3(不再需要 aws-sdk)

Bun.s3 的 API 刻意设计得和 Response / Blob / Bun.file 一致,让"从 S3 读"和"从本地读"体验无差别:

// 配置来自标准 AWS 环境变量:
// AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION / S3_BUCKET

// 上传(直接写,无需先下载到内存)
await Bun.s3.file("uploads/avatar.png").write(
  Bun.file("./avatar.png")
);

// 下载到本地:Bun.write 已原生支持 S3 源
await Bun.write("./downloaded.png", Bun.s3.file("uploads/avatar.png"));

// 列举前缀
for await (const obj of Bun.s3.list("uploads/")) {
  console.log(obj.key, obj.size);
}

官方基准称其 S3 操作比基于 AWS SDK 的 Node 应用快约 5 倍——因为少了 SDK 的层层封装与中间件。

4.6 测试:bun test(内置,零配置)

// src/calc.test.ts
import { test, expect, describe } from "bun:test";

function add(a: number, b: number) {
  return a + b;
}

describe("add", () => {
  test("正整数相加", () => {
    expect(add(1, 2)).toBe(3);
  });

  test("负数", () => {
    expect(add(-1, -1)).toBe(-2);
  });

  test.todo("边界情况待补充");
});

常用姿势:

bun test                      # 运行全部
bun test src/calc.test.ts    # 单文件
bun test --coverage           # 覆盖率
bun test --reporter=junit > report.xml   # 接 CI

bun test 支持快照、内联快照、JUnit / LCOV 报告,基本覆盖了 Jest 的日常用法,却不需要装 Jest。

4.7 打包与构建:bun build

// build.ts
await Bun.build({
  entrypoints: ["./src/client.tsx"],
  outdir: "./dist",
  target: "browser",
  minify: true,
  sourcemap: "external",
  // 原生识别 CSS / 图片 / 字体等资源
});

也可以直接对 HTML 入口打包(前端零配置):

bun build ./public/index.html --production --outdir=./dist

Bun 会在构建时自动调用原生转译器处理 React / Vue 代码,无需额外 babel / webpack 配置。

4.8 进阶:零开销 FFI 与编译期宏

Bun 还有两个"硬核"能力,展示它的底层野心:

(1)bun:ffi 零配置调用 C 库:

import { dlopen, FFIType, suffix } from "bun:ffi";

const lib = dlopen(`libmath.${suffix}`, {
  add: {
    args: [FFIType.f64, FFIType.f64],
    returns: FFIType.f64,
  },
});

console.log(lib.symbols.add(1.5, 2.5)); // 4

(2)Bun 宏:把代码在编译期跑掉

// 在编译期读取 SQL 文件并内联为字符串,避免运行时 IO
import { readSQL } from "./macro" with { type: "macro" };

const query = readSQL("./queries/user.sql");
// macro.ts
import { readFileSync } from "fs";

export function readSQL(path: string) {
  return readFileSync(path, "utf8");
}

宏函数的函数体在编译期执行,返回值被直接内联进产物——这是把"构建时计算"做成语言级特性的思路,和 Zig 的 comptime 哲学一脉相承。


五、性能优化:基准、原理与生产迁移

5.1 自己复现基准,别只信官方数字

任何"快 X 倍"都得自己跑一遍。几个零成本复现脚本:

# 冷启动
time bun -e 'console.log("hi")'
time node -e 'console.log("hi")'

# 包安装(用同一个真实工程)
time bun install
time npm install

# HTTP 吞吐(用 bun 自带的简易压测或 autocannon)
bunx autocannon -c 100 -d 10 http://localhost:3000/api/health

经验结论(我自己在中等规模 API 工程上的观察,非官方):

  • 冷启动 / CLI 场景:Bun 优势明显,Serverless 下能显著降本;
  • 短连接 HTTP API:Bun 的 Bun.serve 吞吐普遍高于 Node + Express;
  • 重计算 / 长连接 / 巨型 SSR:差距收窄,甚至 V8 在某些热点上反超。

所以选型别一刀切:Bun 的甜区是"多、小、快"的服务(边缘函数、API 网关、CLI 工具、原型),而不是"单点重负载"的传统巨石应用。

5.2 把 Node / Express 项目迁到 Bun 的实操清单

  1. 先跑通,再优化:把 package.jsonscriptsnode 换成 bun run,多数纯 JS/TS 项目能直接起;
  2. 替换原生依赖:把 better-sqlite3pg/mysql2(如仅做基础 CRUD)、aws-sdk 逐步换成 bun:sqlite / bun:sql / Bun.s3,减少原生编译与体积;
  3. 移除 babel / webpack:用 bun build 接管;TS 配置里 module 设为 esnextmoduleResolution 设为 bundler
  4. 警惕 Node 专属 APIprocess.bindingrequire 的某些黑魔法、特定 N-API 原生模块可能不兼容,需要逐个验证;
  5. 压测对比:迁移后用 autocannon 跑一遍,记录 P99 与内存,再决定是否全量切。

5.3 性能调优的五个着眼点

  • 减少序列化:能用 Bun.file 直接流式返回,就别 await resp.text()Buffer.from
  • 用同步 SQLite 替代网络往返:配置、元信息、会话等小数据,本地 SQLite 比远程 KV 快一个量级;
  • 复用 db.query 预编译语句:别每次请求拼新语句;
  • WebSocket 代替轮询:Bun 的 WS 实现轻量,长连接场景省资源;
  • 生产构建务必 --minify + 外部 sourcemap:既保体积又保可调试性。

六、总结与展望:Bun 到底值不值得 All-in

把 Bun 的 2026 摆在桌面上,我的判断是审慎乐观

值得跟进的理由

  1. 开发者体验断层领先:一个二进制解决 runtime + 包管理 + 打包 + 测试,新项目从 0 到能跑的时间被压到极致;
  2. 性能甜区真实存在:冷启动、包安装、轻量 HTTP 这三块,Bun 不是"微优化",而是数量级差异;
  3. 全栈 API 收编降低心智负担bun:sqlBun.s3bun:sqlite 让"写个带存储的全栈服务"从"装五个库"变成"写几行";
  4. Rust 重写抬高可靠性基线:从 Zig 的"内存 bug 难修"到 Rust 的"编译期保证",这对一个基础设施级项目是长期利好;
  5. Anthropic 的押注:意味着 Bun 会持续拿到 AI 原生工具链的红利与资源。

需要警惕的风险

  1. 整合边界过宽:内置模块能否追上专业库多年的边界处理,需要时间验证;
  2. 兼容性并非 100%:真实项目里总有 Node 专属黑魔法会绊住你;
  3. 治理与节奏争议:半年内从"Zig 特立独行"到"Rust 重写",再叠加"加入 Anthropic",节奏激进,部分社区已用脚投票(如 yt-dlp 弃用);
  4. AI 重写的"可维护性"未知数:六天百万行、99.8% 通过率很燃,但长期可维护性、可读性、团队接手成本,要等社区检验。

15 条生产踩坑清单

  1. 不要盲目全量替换 Node:先在非核心服务试点,跑满一周再决定;
  2. bun:sqlite 是同步的:高并发写场景注意锁竞争,必要时用 WAL 模式;
  3. bun:sql 不同数据库方言有差异:分页、JSON 函数、自增语法别假设一致;
  4. Bun.s3 配置走环境变量:别把密钥写进代码,用 Bun.secrets 或 CI Secret;
  5. 低版本 Bun 有已知安全坑:生产务必锁定 v1.3.x 及以上;
  6. 原生 N-API 模块可能不兼容bcryptsharp 等需逐个验证或找替代;
  7. bun.lock 是文本锁:升级 Bun 后记得重新 bun install 刷新;
  8. HMR 在复杂状态里有边界:有状态模块用 import.meta.hot 显式处理 dispose;
  9. WebSocket 升级必须返回 undefined:忘了返回会导致连接挂起;
  10. Bun.build 默认不打包 node_modules:需显式配置 external / bundle;
  11. FFI 调用注意 ABI 与平台后缀:用 suffix 自动适配 .dylib / .so / .dll
  12. 宏函数体在编译期执行:别在里面写有副作用或依赖运行时的逻辑;
  13. 压测前先warm-up:JSC 的 JIT 需要预热,冷压测会低估性能;
  14. 生产构建开 --minify 但保留 sourcemap:线上排障靠它;
  15. 关注 Rust 重写后的版本稳定性:v1.3.14 这类过渡版本,先观察一个 minor 再上生产。

写在最后

Bun 的故事,本质上是一个关于**"整合 vs 专注"**的永恒 engineering 辩题,在 2026 年的一次激进作答。它用 JavaScriptCore 和 Zig/Rust 把"快"做成了基础设施,又用 All-in-One 把"省心"做成了产品体验;而那场 AI 驱动的百万行 Rust 重写,则让"重写一个生产级运行时"第一次看起来不再是不可能的任务。

我不会鼓动你明天就把公司所有 Node 服务换成 Bun——那不负责。但我强烈建议你:新项目、CLI、边缘函数、内部工具,从今天起默认试试 Bun。当你体验到 bun install 几秒完成、bun test 零配置跑通、bun:sql 一套代码打通 PG 和 MySQL 的那一刻,你可能就回不去那个要装 eight 个工具链的 world 了。

毕竟,工具存在的意义,就是让正确的事变得理所当然地简单。

推荐文章

IP地址获取函数
2024-11-19 00:03:29 +0800 CST
html流光登陆页面
2024-11-18 15:36:18 +0800 CST
总结出30个代码前端代码规范
2024-11-19 07:59:43 +0800 CST
linux设置开机自启动
2024-11-17 05:09:12 +0800 CST
thinkphp分页扩展
2024-11-18 10:18:09 +0800 CST
程序员茄子在线接单