Bun 是近年来成长最快的 JavaScript 运行时 之一。它用 Zig 语言编写、底层基于 Apple 的 JavaScriptCore 引擎,从一开始就瞄准「更快的 Node.js 替代」这一目标——不仅运行时本身快,还把包管理、测试、打包、HTTP 服务全部内置,试图用一个二进制替代 npm、jest、esbuild、nodemon 等一整套工具链。本文带你从安装到生产落地把 Bun 真正跑起来。
一、为什么还需要一个新的运行时
Node.js 统治服务端 JavaScript 已十余年,但在启动速度、冷启动内存、内置 API 完整度上存在明显短板:原生没有内置测试框架,需要引 jest;没有内置打包,需要引 esbuild 或 webpack;TypeScript 必须过一层编译;就连最基础的 HTTP 服务也得依赖 Express、Koa 这类第三方库。当 Serverless、边缘函数把「冷启动」变成核心指标时,Node 的短板被进一步放大。
Bun 的出现正是想把这些「标配能力」直接塞进运行时本身。它不是又一个跑在 V8 上的兼容层,而是用 Zig 重写了运行时内核,并选择启动极快的 JavaScriptCore 作为引擎。结果是:安装即用、运行即快、工具链自带。
二、Bun 到底是什么
Bun 的核心是一体化的工具链:运行时(兼容 Node.js 大部分 API)、包管理器(bun install 比 npm 快数倍)、测试运行器(bun test)、打包器(bun build),以及 Bun.serve 提供的高性能 HTTP 服务。它默认支持 TypeScript、JSX,无需额外配置即可直接运行 .ts 文件,对前端开发者极其友好。
2.1 一个二进制替代一整套工具
过去一个标准前端/Node 工程至少要装 node、npm/yarn、jest、esbuild、ts-node;换成 Bun 之后,这些角色都被一个 bun 命令覆盖。CI 镜像体积、依赖安装时间、本地环境不一致导致的「在我机器上能跑」问题,都能明显缓解。
2.2 与 Node.js 的兼容性边界
Bun 实现了 Node 的绝大部分 API(fs、path、process、Buffer、stream 等),绝大多数纯 JS 库可以直接跑。但部分用 C++ 编写的 .node 原生插件仍可能不兼容,迁移存量大型项目时需重点验证这一类依赖。
三、安装与版本管理
官方推荐通过 curl 一键安装,也可以用 npm 或 Homebrew。Bun 是单文件二进制,升级本质上就是覆盖二进制,因此非常适合在容器里固定版本。
# 官方一键安装
curl -fsSL https://bun.sh/install | bash
# 或用 Homebrew
brew install oven-sh/bun/bun
# 验证
bun --version
四、直接运行 TypeScript(无需编译)
Bun 内置了 TypeScript 转译器,直接 bun run 即可执行 .ts 文件,不需要 tsc 或 ts-node。下面是一段用内置 fetch(遵循 Web 标准)请求接口的例子:
// hello.ts
const res = await fetch("https://api.example.com/health");
const data = await res.json();
console.log("status", data);
// 直接运行,无需任何编译步骤
bun run hello.ts
五、内置测试:bun test
不再需要 jest。Bun 自带测试运行器,语法与 expect 风格一致,启动是毫秒级,且自带覆盖率统计。对一个工具库做单元验证,体验比传统方案顺滑很多。
// sum.test.ts
import { expect, test } from "bun";
test("adds numbers", () => {
expect(1 + 1).toBe(2);
});
// 运行全部测试
bun test
# 带覆盖率
bun test --coverage
六、包管理:bun install
bun install 采用全局缓存 + 硬链接的方式,首次安装可能受网络影响,但二次安装几乎瞬间完成。生产环境强烈建议用 –frozen-lockfile 锁定依赖树,保证构建可复现。
# 安装依赖(远快于 npm install)
bun install
# CI 中锁定版本,保证可复现
bun install --frozen-lockfile
七、内置打包:bun build
生产构建可以直接用 bun build,API 风格类似 esbuild,能够把 TS/JSX 打包成单文件或目录,也能在代码里以 API 方式调用。
# 命令行打包
bun build ./src/index.ts --outfile=dist/app.js --target=node
# 或在代码里调用打包 API
import { build } from "bun";
await build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
});
八、用 Bun.serve 写 HTTP 服务
Bun 内置的高性能 HTTP 服务基于 uWS 风格的实现,无需 Express 也能直接起服务,并天然支持 WebSocket,非常适合写轻量级 API 与实时网关。
Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/health") {
return Response.json({ ok: true });
}
return new Response("Hello from Bun!");
},
});
console.log("listening on :3000");
九、Bun 与 Node.js、Deno 怎么选
三者定位并不完全重叠,下面这张表帮你快速判断该把 Bun 放在什么位置:
| 维度 | Bun | Node.js | Deno |
|---|---|---|---|
| 底层引擎 | JavaScriptCore | V8 | V8 |
| 启动速度 | 最快 | 一般 | 较快 |
| 内置工具链 | 包管理/测试/打包/HTTP | 无(依赖生态) | 测试/格式化/权限 |
| TS 支持 | 原生 | 需编译 | 原生 |
| Node 兼容 | 较高 | 原生 | 逐步兼容 |
十、生产落地检查清单
如果你打算在服务器上用 Bun 跑线上服务,建议逐项核对,避免踩到环境不一致的坑:
| 检查项 | 建议 |
|---|---|
| 版本固定 | 用 bunfig.toml 或 CI 锁定版本,避免自动升级引发副作用 |
| 进程守护 | 配合 systemd 或容器,进程崩了能自动拉起 |
| 依赖安装 | CI 用 bun install –frozen-lockfile 保证可复现 |
| 健康检查 | 暴露 /health 端点,供反向代理探活 |
| 反向代理 | 前面挂 Nginx/Caddy 做 TLS 终止与静态资源 |
| 可观测 | 用 Prometheus 抓取指标,配 Grafana 看板监控 |
十一、常见报错与排查
迁移期最容易遇到的是原生模块兼容问题。若某个 npm 包依赖 C++ addon,可先尝试用 npm 安装该包后让 Bun 复用 node_modules;其次是 ESM/CJS 混用导致的 import 报错,Bun 默认优先 ESM,老项目的 require 语法建议加 “type”:”module” 或在文件里改用 import;最后,生产环境尽量用 bun build 产出独立 bundle,避免运行时再去解析 TS,从而降低冷启动与内存占用。
# 依赖安装失败时用 npm 兜底
npm install
# 然后用 Bun 运行,复用 node_modules
bun run start
# 产出独立可执行 bundle
bun build ./src/index.ts --compile --outfile=server
十二、局限与适用场景
Bun 并非银弹。它对 Node 生态的兼容虽高,但部分重度依赖 Node 内部行为的老库仍可能踩坑;一些云厂商 SDK 的原生插件也需要实测。对于新项目、CLI 工具、高频冷启动的 Serverless/边缘函数,Bun 的收益最明显;存量大型单体服务迁移则需充分测试与灰度。
如果你已经用 Vite 构建前端,或打算用 Web Components 封装 UI,Bun 在本地开发与构建环节都能顺滑衔接;把它作为 前端数据请求 的后端 API 服务也相当轻量。部署时配合 Caddy 自动 HTTPS 或 Nginx 反向代理,再加上 GitHub Actions 做持续集成,即可组成一条现代前端全栈流水线。
十三、从零跑通一个最小服务
把上面的能力串起来,一个最小可上线的 Bun 服务其实只需要几十行:用 Bun.serve 提供接口,用 bun build 产出 bundle,前面挂一层反向代理做 TLS。比起传统 Node + 一堆依赖的栈,Bun 让「写一个小而快的服务」这件事重新变得轻巧。




