目录

Bun 深度拆解:用 Zig + JavaScriptCore 把 Node.js 生态压成一个二进制

Bun 深度拆解:用 Zig + JavaScriptCore 把 Node.js 生态压成一个二进制

核心判断

Bun 不只是「更快的 Node.js」——它是一个用 Zig 写的 JavaScript 运行时 + 内嵌包管理器 + bundler + 转译器 + test runner 的单体可执行文件。当下社区最值得关注的是它在 94K+ stars 背后真正解决的工程问题:降低 Node.js 工具链的整体复杂度。它不是 Node.js 的「竞品」,而是 Node.js 安装体验(nvm + npm + node-gyp + webpack + jest + tsx)的「整合方案」。

项目速览

为什么值得看

Node.js 生态长期存在一个隐性成本——为了让一段 TS 代码跑起来,你至少需要 node(运行时)+ npm(包管理)+ tsc/tsx(转译)+ webpack/vite(打包)+ jest/vitest(测试),每一层都有自己的配置文件和包管理目录。Bun 把这五件事压到一个二进制里,代价是放弃一部分 Node.js 兼容性,收益是启动时间和冷启动体积的显著下降

系统地图

┌──────────────────────────────────────────────────────────┐
│                       bun CLI                            │
├──────────────┬──────────────┬───────────────┬────────────┤
│  bun run     │  bun install │  bun test     │  bun build │
│  (script)    │  (package    │  (test runner │  (bundler) │
│              │   manager)   │   + Jest API) │  + trans-  │
│              │              │               │  piler)    │
├──────────────┴──────────────┴───────────────┴────────────┤
│                Bun Runtime (Zig + Rust)                  │
├───────────────────────┬──────────────────────────────────┤
│  JavaScriptCore       │   Node-API (N-API) compatibility  │
│  (WebKit, JS engine)  │   + Web Standards (fetch/         │
│                       │   WebSocket/ReadableStream)      │
└───────────────────────┴──────────────────────────────────┘

关键机制

1. JavaScriptCore,不是 V8

Bun 选择 JavaScriptCore(WebKit 的 JS 引擎)而不是 V8(Chrome/Node.js 的引擎)。原因有三:

  • 启动更快:JavaScriptCore 的启动路径比 V8 短,适合 CLI 频繁冷启动的场景(bun run index.ts)。
  • 内存更省:V8 的 hidden class 和 inline cache 优化侧重长时间运行的 Web 页面,Bun 的 serverless-style 短进程场景用 JSC 更合适。
  • C/C++ 集成更简单:JavaScriptCore 的 API 比 V8 更适合从 Zig 调用——Bun 大量使用 Zig 的 C ABI 互操作。

代价:极少数 Node.js 库依赖 V8 特有的 C++ 扩展(node-gyp 原生模块),Bun 走的是 N-API(Node-API)路径,只支持 N-API 兼容的原生模块。

2. Zig 作为实现语言

Bun 的核心用 Zig 写,而非更常见的 Rust 或 C++。Zig 的核心优势:

  • 零隐藏控制流:@asyncCall / await 没有隐式内存分配,适合运行时这种对延迟敏感的场景。
  • 编译期求值:comptime 关键字让很多「C++ 模板」的工作在 Zig 里直接写。
  • C ABI 友好:Zig 可以直接调用 .a / .so / .dylib,不需要 FFI 库。Bun 链接 JavaScriptCore 时就是直接调 C API。

注意:Bun 的部分模块(如 npm 兼容层)用 Rust 写,因为 npm 生态里有大量现成的 Rust crate 可以复用。

3. bun install:二进制 lockfile + 符号链接

bun installnpm install 快 5-30 倍的关键:

  • 二进制 lockfile:bun.lockb 是二进制格式,读写比 package-lock.json 快一个量级。
  • 全局内容寻址存储:所有下载的包放进 ~/.bun/install/cache/,不同项目共享同一份 tarball,只通过符号链接引用。npm 是每个项目独立 node_modules/
  • 并行解析 + 安装:在包解析阶段就并行下载,而不是串行。

代价:bun.lockb 不能直接 diff / merge,需要团队约定或者使用 bun install --save-text-lockfile

4. 内置 TypeScript 和 JSX

Bun 直接执行 .ts / .tsx 文件,无需 tsc 编译。内部用了一个自定义的 TypeScript 转译器(非官方编译器,只去类型 + 转换语法),启动时间远小于 ts-node / tsx

bun run index.tsx    # 直接跑,不需 tsc

类型检查仍然要 tsc --noEmit,Bun 不替代类型检查。

5. 内置 test runner,Jest 兼容 API

bun test 实现 Jest 的 describe / it / expect / mock API,但底层用 Bun 自己的运行时,启动比 Jest 快 5-10 倍。对于单测覆盖率工具链,Jest 生态(@testing-library/*)基本兼容。

适用边界

适合用 Bun 的场景:

  • CLI 工具、Docker 镜像体积敏感的服务(bun:alpine 镜像比 node:alpine 小约 60%)。
  • 高 QPS 短请求的 API(serverless、edge runtime),启动时间主导整体延迟。
  • 本地开发脚本、TypeScript 直接执行的场景,不想配 ts-node / tsx。

不建议用 Bun 的场景:

  • 重度依赖特定 V8 C++ 扩展(如某些数据库驱动)的项目——Bun 走 N-API,部分原生模块可能无法加载。
  • 必须严格遵循 Node.js LTS 兼容性承诺的金融/政企场景——Bun 仍处于快速迭代期,API 可能变动。
  • 需要 npm audit / npm ci 严格一致性的 CI/CD 流水线(虽然 Bun 也支持 bun ci,但生态成熟度不及 npm)。

上手示例

# 安装(Linux/macOS)
curl -fsSL https://bun.com/install | bash

# 新建项目
bun init my-app && cd my-app

# 安装依赖(快于 npm 5-30x)
bun install

# 运行 TS 文件(无需 tsc)
bun run index.ts

# 测试(Jest API)
bun test

# 升级
bun upgrade

总结

Bun 的真正价值不是「性能数字」,而是用一份二进制文件替代 Node.js 工具链的碎片化。94K stars 说明开发者愿意为这种整合付出兼容性代价。短期内它不会取代 Node.js,但在容器化、serverless、CLI 等场景,它已经成为值得评估的默认选项。

参考