Bun:一个二进制文件,干掉了整个 JavaScript 工具链

一个典型的现代 JavaScript 项目需要多少工具?

Node.js 作为运行时,npm 或 yarn 管理依赖,TypeScript 编译器处理 .ts 文件,ts-node 让你在开发时不用手动编译,webpack 或 vite 打包代码,jest 或 vitest 跑测试,dotenv 加载环境变量,nodemon 监听文件变化热重载……每个工具解决一个问题,每个工具有自己的配置文件,每个工具的安装和版本管理都是额外的认知负担。

Bun 的立场很直接:这些东西本来就应该在一起

一、Bun 是什么

Bun 是一个 JavaScript/TypeScript 的全栈工具包,包含:

  • 运行时:执行 JS/TS 代码,类似 Node.js
  • 包管理器bun install,类似 npm/yarn/pnpm
  • 打包器bun build,类似 webpack/esbuild/vite
  • 测试框架bun test,Jest 兼容的 API

这四件事,Node.js 生态里需要四套不同的工具来完成。Bun 把它们装进了一个二进制文件。

安装之后的体验是:

# 直接运行 TypeScript,不需要 ts-node 或者先编译
bun run server.ts

# 安装依赖(比 npm install 快 10-25 倍)
bun install

# 运行测试(Jest 兼容的 API,不需要安装 jest)
bun test

# 打包为生产版本
bun build ./src/index.ts --outdir ./dist

没有 tsconfig.json 的 TypeScript 支持,没有 .babelrc,没有单独的测试框架安装,没有 nodemon。开箱即用。

二、为什么快:引擎、语言、设计三层加速

JavaScriptCore 而不是 V8

Node.js 和 Deno 都使用 Google 的 V8 引擎。Bun 选择了 JavaScriptCore(JSC)——WebKit 的 JS 引擎,也就是 Safari 用的那个。

JSC 的设计目标和 V8 有所不同。V8 针对长时间运行的服务端场景优化了吞吐量,而 JSC 最初是为浏览器设计的,对启动速度和内存占用更敏感。这让 Bun 在启动延迟上有明显优势——跑一个 CLI 工具或者测试套件,Bun 的冷启动比 Node.js 快很多,这在频繁执行短命令的场景里(CI、脚本)非常明显。

Zig 写的底层实现

Bun 的核心用 Zig 写成(这也是为什么我们之前聊 Zig 时把 Bun 当做代表性案例)。Zig 的优势在这里体现在两点:

一是极薄的抽象层。Node.js 的很多内置模块是 C++ 写的,C++ 代码和 JS 引擎之间有一定的 FFI 开销。Zig 和 C 的互操作几乎没有额外开销,这让 Bun 的内置 API(文件系统、HTTP、SQLite)执行效率更高。

二是内存控制。Zig 没有 GC,Bun 的内存分配是精确可控的,不会有 GC 暂停导致的延迟抖动。

原生 TypeScript 支持

Node.js 运行 TypeScript 需要先把 .ts 文件编译成 .js,再执行 .js。这个编译步骤要么在运行时做(ts-node 的做法,慢),要么提前做(生产构建的做法,但开发体验差)。

Bun 把 TypeScript 的类型剥离(Type Stripping)直接内置到运行时里——它读取 .ts 文件,删掉类型注解,直接执行剩下的 JS 代码,不做完整的类型检查,也不需要 tsc。这让 TypeScript 文件的执行速度和纯 JS 几乎一样快。

注意:Bun 不做完整的类型检查,它只做类型剥离。如果你需要类型安全,还是要单独跑 tsc --noEmit。但在开发和执行阶段,不需要等编译。

三、包管理器:bun install 有多快

这是 Bun 最容易直接感受到的优势。

在一个有几百个依赖的典型 Next.js 项目里,npm install 通常需要 30-60 秒,yarn install 需要 20-40 秒,bun install 通常在 2-5 秒。差距在 10-25 倍量级。

这个速度差距来自几个地方:

并行下载和提取。npm 的下载和解压是分步的,bun 同时做多件事。

全局缓存。bun 有一个机器级别的全局缓存(~/.bun/install/cache),同一个包的同一个版本只下载一次,后续安装直接从缓存复制(实际上是硬链接,几乎是零成本)。

二进制锁文件。bun 的 lockfile(bun.lockb)是二进制格式,比 npm 的文本 JSON 解析快得多。

更高效的依赖解析算法。Bun 的依赖解析用 Zig 从头实现,比 npm 的 JavaScript 实现快很多。

bun install 还完全兼容 npm registry 和 package.json 格式,切换成本几乎为零。

四、几个被低估的内置能力

原生 SQLite 支持

Bun 内置了一个高性能的 SQLite 驱动(bun:sqlite),不需要安装任何 npm 包:

import { Database } from "bun:sqlite";

const db = new Database("mydb.sqlite");
const query = db.query("SELECT * FROM users WHERE age > $age");
const users = query.all({ $age: 18 });

这对快速原型开发和轻量级应用非常有用——不需要配置外部数据库,不需要安装驱动包,直接用。

自动加载 .env

Bun 自动读取 .env 文件,不需要安装 dotenv 包,不需要在代码里 require('dotenv').config()。直接用 process.env.MY_VAR 就能读到。

内置 WebSocket 服务器

Bun.serve({
  port: 3000,
  fetch(req, server) {
    if (server.upgrade(req)) return; // WebSocket 升级
    return new Response("Hello!");
  },
  websocket: {
    message(ws, message) {
      ws.send(`Echo: ${message}`);
    },
  },
});

HTTP 和 WebSocket 服务器都内置了,不需要 ws 包。

五、Node.js 兼容性:够用但不完美

Bun 声称与 Node.js 高度兼容,大多数 npm 包可以直接在 Bun 里运行。实际情况是:对于大多数应用层代码,兼容性足够好;对于需要深度使用 Node.js 原生模块或特定内部 API 的包,可能有问题

Bun 已经实现了大量 Node.js 核心模块:fspathhttphttpsstreamcryptobufferchild_process……覆盖率持续提高。

常见的兼容性问题场景:

  • 某些依赖 C++ 原生插件(.node 文件)的包,Bun 不支持
  • 使用 vm 模块做代码沙箱的包,行为可能有差异
  • 依赖特定 V8 API 的包(这类包本来就不多)

实际迁移时,建议先把测试跑通,再考虑生产部署。大多数 Express、Fastify、Hono 等 Web 框架项目迁移到 Bun 都相对顺畅。

六、什么时候用 Bun,什么时候不用

最适合 Bun 的场景:

新项目,特别是 API 服务、CLI 工具、脚本。从零开始用 Bun,你得到的是更快的启动速度、原生 TypeScript 支持、更快的依赖安装,同时不需要配置任何额外工具。

需要高并发 HTTP 处理的场景。Bun 的 HTTP 服务器在基准测试中通常比 Node.js 快 2-4 倍,因为底层实现更高效。

重度使用 SQLite 的轻量应用(本地工具、内嵌数据库应用)。

谨慎使用 Bun 的场景:

依赖大量 npm 包的复杂项目,特别是有 C++ 原生插件依赖的。兼容性风险需要提前验证。

对稳定性要求极高的关键生产系统。Bun 还相对年轻(2023 年发布 1.0),踩到边缘 bug 的概率比 Node.js 高。Node.js 已经经历了十几年的生产验证。

需要最高峰值吞吐量的 CPU 密集型计算。V8 的 JIT 编译在长时间运行的热代码上可能比 JSC 有更高的峰值性能(两个引擎各有优劣,JSC 启动快,V8 峰值高)。

七、和 Deno 的对比

经常有人把 Bun 和 Deno 放在一起比较,因为两者都是"现代 Node.js 替代方案"。但两者的立场其实不同:

Deno 的立场是"重新设计 JavaScript 服务端运行时"——内置 TypeScript、Web 标准 API、权限系统(默认沙箱)、去掉 node_modules。Deno 更愿意打破兼容性来追求更干净的设计。

Bun 的立场是"在现有生态上提供更好的体验"——Node.js 兼容、npm 兼容、现有项目可以迁移。Bun 更愿意保持兼容性来降低迁移成本。

结果是:Deno 更适合从头开始的项目,且你接受它的生态相对小的代价;Bun 更适合从现有 Node.js 项目迁移,或者想用 npm 生态但要更快的体验。

八、总结

Bun 解决的核心问题是:JavaScript 工具链太碎了。它用一个二进制文件提供了:

  • 运行时(替代 Node.js)
  • TypeScript 支持(替代 ts-node/tsc)
  • 包管理器(替代 npm/yarn,快 10-25 倍)
  • 测试框架(替代 Jest,零配置)
  • 打包器(替代 webpack/esbuild,简单场景)
  • 内置 SQLite、.env 加载、WebSocket 服务器

对于新项目,特别是 TypeScript 为主的 API 服务和工具,Bun 提供的开发体验明显更好。对于现有的大型 Node.js 项目,迁移成本需要仔细评估。

Bun 真正的意义不只是"快",而是它提出了一个问题:为什么 JavaScript 生态需要十几个工具来完成一件事? 这个问题的提出本身,已经在推动整个 JS 工具链生态重新审视自己的碎片化问题。