如果你的项目每次
npm install要等一分钟,每次跑测试要等十几秒,每次启动开发服务器要盯着终端发呆------你应该试试 Bun。这篇文章不吹不黑,直接上手把一个真实项目从 Node.js 迁到 Bun,把迁移步骤、踩过的坑、适不适合你的项目全部写清楚。
Bun 是什么,为什么 2026 年该试试
如果你还不知道 Bun------它是一个用 Zig 语言写的 JavaScript 运行时,目标是做 Node.js 的替代品。
一句话总结它和 Node.js 的关系:
Bun 之于 Node.js,就像 Vite 之于 Webpack------做同样的事,但快得多。
它不只是一个运行时,而是一个全家桶:
| 功能 | Node.js 生态 | Bun 内置 |
|---|---|---|
| 运行时 | Node.js | Bun |
| 包管理器 | npm / pnpm / yarn | bun install |
| 打包器 | Webpack / Vite / esbuild | bun build |
| 测试框架 | Jest / Vitest | bun test |
| TypeScript | 需要 ts-node / tsx | 原生支持,直接跑 .ts |
| .env 加载 | 需要 dotenv 包 | 原生支持 |
2026 年的 Bun 已经到了 1.2+ 版本,稳定性比两年前好了很多。大部分 npm 包都能直接用。
迁移前后数据对比
Bun 官方跑的 benchmark 和社区实测都指向同一个结论:快,而且不是快一点。
以一个中等规模的全栈项目(React + Express,约 200 个 npm 依赖)为参考:
| 操作 | Node.js 20 + npm | Bun 1.2 | 差距 |
|---|---|---|---|
install(无缓存) |
约 40-60s | 约 3-5s | 10-15x |
install(有缓存) |
约 10-15s | < 1s | 20x+ |
| 开发服务器启动 | 约 5-10s | 约 1-3s | 3-5x |
| 单测(100+ 用例) | 约 10-15s | 约 3-5s | 3x |
| TypeScript 编译 | 需要额外配置 ts-node | 原生支持 | --- |
| 冷启动一个 HTTP 接口 | 约 200-400ms | 约 50-100ms | 3-4x |
具体数值取决于你的机器配置和项目大小,但量级差距是一致的。你可以在自己项目里跑一下对比看看。
迁移步骤(20 分钟搞定)
第一步:安装 Bun
bash
# macOS / Linux
curl -fsSL https://bun.sh/install | bash
# 验证安装
bun --version
# 1.2.x
Windows 用户用 WSL2 安装。Bun 对 Windows 原生的支持在 1.2 版本已经基本完善。
第二步:用 Bun 替换 npm
bash
# 删掉 node_modules 和 lock 文件
rm -rf node_modules package-lock.json yarn.lock pnpm-lock.yaml
# 用 Bun 重新安装依赖
bun install
这一步会生成 bun.lockb(Bun 的 lock 文件,二进制格式,比 JSON 快得多)。
第三步:替换 package.json 里的 scripts
json
{
"scripts": {
"dev": "bun run --hot src/server.ts",
"build": "bun build src/index.ts --outdir dist",
"test": "bun test",
"start": "bun src/server.ts"
}
}
关键变化:
node src/server.js→bun src/server.ts(直接跑 TypeScript,不需要 ts-node)nodemon→bun run --hot(内置热重载)jest/vitest→bun test(内置测试框架,API 兼容 Jest)
第四步:测试跑一遍
bash
bun test
bun run dev
大多数项目到这一步就可以正常跑了。如果遇到问题,看下面的踩坑记录。
踩坑记录
坑 1:某些 Node.js 原生模块不兼容
我的项目用了 bcrypt(密码哈希),它是 C++ 写的 native addon。Bun 对大部分 native addon 已经支持了,但 bcrypt 的某个版本有问题。
解决方案: 换成纯 JS 实现的 bcryptjs。
bash
bun remove bcrypt
bun add bcryptjs
代码改动只有 import 路径:
javascript
// 之前
import bcrypt from 'bcrypt';
// 之后
import bcrypt from 'bcryptjs';
API 完全一致,不需要改业务代码。性能差距在大多数场景下可以忽略。
通用原则: 如果某个包有 C++ native addon 版本和纯 JS 版本,迁移到 Bun 时优先选纯 JS 版本。
坑 2:__dirname 和 __filename 行为差异
Bun 支持 __dirname 和 __filename,但在 ES Module 模式下行为和 Node.js 有细微差异。
如果你的代码里有这种写法:
javascript
import path from 'path';
const configPath = path.join(__dirname, '../config/default.json');
在 Bun 的 ESM 模式下可能报错。
解决方案: 用 import.meta 替代。
javascript
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const configPath = path.join(__dirname, '../config/default.json');
这个写法同时兼容 Node.js 和 Bun。
坑 3:.env 不需要 dotenv 了
Bun 原生支持 .env 文件加载,不需要 dotenv 包。
如果你的代码里有:
javascript
import 'dotenv/config';
// 或
require('dotenv').config();
在 Bun 里可以直接删掉这行。Bun 会自动加载项目根目录的 .env 文件。
但要注意:如果你同时有 .env 和 .env.local,Bun 的加载优先级和 dotenv 不一样。 Bun 的优先级是:
bash
.env.local > .env.production / .env.development > .env
建议:迁移后检查一下环境变量是否都正确加载了。
坑 4:bun test 和 Jest 的语法差异
bun test 的 API 和 Jest 基本兼容,但有几个地方不同:
javascript
// Jest 写法(在 Bun 中也能跑)
expect(fn).toHaveBeenCalledWith(expect.objectContaining({ id: 1 }));
// 但这个 Jest 特有的 mock 方式不支持:
jest.mock('./database');
// Bun 的 mock 方式:
import { mock } from 'bun:test';
mock.module('./database', () => ({
query: mock(() => Promise.resolve([]))
}));
解决方案: 如果你的测试大量依赖 jest.mock,建议暂时保留 Vitest 或 Jest 来跑测试,其他部分用 Bun。Bun 可以和现有测试框架共存。
哪些场景适合迁移,哪些不适合
| 场景 | 是否建议迁移 | 原因 |
|---|---|---|
| 个人项目 / side project | ✅ 强烈推荐 | 没有历史包袱,直接享受速度 |
| 新项目 | ✅ 推荐 | 从零开始用 Bun,避免迁移成本 |
| 中小型全栈项目 | ✅ 可以 | 大部分 npm 包已兼容 |
| 大型企业项目 | ⚠️ 谨慎 | native addon 多,CI/CD 需要适配 |
| 依赖大量 native addon | ❌ 暂不建议 | sharp、canvas 等可能有兼容问题 |
| Serverless / Edge 部署 | ✅ 推荐 | Bun 冷启动极快,适合 serverless |
迁移检查清单
用这个清单确认迁移是否完整:
-
bun install安装成功,无报错 -
bun run dev开发服务器正常启动 -
bun test测试全部通过 -
.env环境变量正确加载 - 所有 API 接口手动测试通过
- 构建产物
bun build正常输出 - CI/CD 流水线里的 Node.js 镜像换成 Bun 镜像
-
Dockerfile基础镜像改为oven/bun:1.2 -
package.json里的 scripts 都更新了 - 删掉不再需要的包:
dotenv、ts-node、nodemon
迁移后可以卸载的包
迁移到 Bun 后,这些包可以直接删掉:
bash
bun remove dotenv ts-node tsx nodemon ts-jest
| 包名 | 原因 |
|---|---|
dotenv |
Bun 原生支持 .env |
ts-node / tsx |
Bun 原生跑 TypeScript |
nodemon |
bun run --hot 内置热重载 |
ts-jest |
bun test 原生支持 TypeScript |
少了这些依赖,node_modules 又小了一圈。
写在最后
迁移整个项目大概 20 分钟,踩坑修复算上也就 1 个多小时。
换来的是每天开发过程中安装快十几倍、启动快好几倍、测试快 3 倍。这种速度差距体验过就回不去了。
如果你还在犹豫,建议先拿一个 side project 试试。20 分钟的迁移成本,几乎零风险。
你的项目目前用什么运行时和包管理器?有考虑过迁移到 Bun 吗?评论区聊聊。