Bun 快速入门:项目、依赖与测试
bun init 创建项目、bun add 管理依赖、bun test 编写测试,完整走一遍小工具开发。
0. 一句话理解
Bun 的项目还是
package.json+node_modules那套(生态兼容),只是执行更快、命令更短、测试内置。
本篇以”演出海报文件夹整理工具”为背景,把建项目、装依赖、写测试、打包四件事走一遍——这套动作在任何 Bun 项目里都一样。
1. 初始化项目
bun init -y
package.json index.ts tsconfig.json .gitignore
讲解:
bun init生成package.json、index.ts、tsconfig.json与.gitignore;-y跳过交互提问。- 生成的
index.ts里有一个可运行的server示例,bun run index.ts立刻能看到效果。 - 对比
npm init:多出了开箱即用的 TypeScript 配置,无需再装typescript与ts-node——bun run与bun test都原生理解 TS。
2. 管理依赖
bun add hono # 运行时依赖
bun add -d @types/bun # 开发依赖(-d 等价 npm 的 --save-dev)
bun remove hono # 卸载
bun add zod@3 # 安装指定版本段
讲解:
bun add安装并写入package.json的 dependencies;-d写入 devDependencies,bun remove卸载。- 锁文件是文本格式的
bun.lock(Bun 1.2 起,取代旧二进制bun.lockb),可读、可评审、合并冲突好处理;提交 git 保证环境一致。 - 安装速度快的原理:并行下载 + 全局内容寻址缓存——同一版本的包文件在全局只存一份,项目里用硬链接指过去,重复安装近乎零成本。
- 团队约定一条:同一项目只用一个包管理器。bun.lock 与 package-lock.json 并存会导致两套工具解析出不同版本,“我这能跑”的经典事故由此而来。
3. 内置测试框架
// math.ts
export function factorial(n: number): number {
if (n < 0) throw new Error("负数没有阶乘")
let result = 1
for (let i = 2; i <= n; i++) result *= i
return result
}
// math.test.ts
import { describe, expect, test } from "bun:test"
import { factorial } from "./math"
describe("factorial", () => {
test("0 的阶乘是 1", () => {
expect(factorial(0)).toBe(1)
})
test("5 的阶乘是 120", () => {
expect(factorial(5)).toBe(120)
})
test("负数抛错", () => {
expect(() => factorial(-1)).toThrow("负数")
})
})
bun test
math.test.ts:
pass factorial > 0 的阶乘是 1
pass factorial > 5 的阶乘是 120
pass factorial > 负数抛错
3 pass
(0ms)
讲解:
bun:test提供与 Jest 几乎相同的 API:describe/test/expect;Jest 项目迁移基本是”改 import 来源”这一步。- 测试文件命名
*.test.ts(或*_test.ts)会被自动发现,不需要配置文件;要排除某个目录用bunfig.toml配置。 - 三个用例分别覆盖:边界(0)、正常(5)、异常(负数)——这是测试设计的标准三分法。
bun test默认并发运行测试文件;1.3.13 起--parallel可跨多个 worker 进程分发,大型仓库整体提速更明显。- mock、快照、覆盖率等进阶用法(
jest.fn风格的mock()、--coverage)见内置测试与基准。
4. scripts 与任务
// package.json
{
"name": "poster-tools",
"type": "module",
"scripts": {
"dev": "bun --watch index.ts",
"start": "bun index.ts",
"test": "bun test",
"build": "bun build ./index.ts --outdir dist --target bun"
}
}
讲解:
bun --watch index.ts监听文件变化自动重启,相当于 Node 生态的 nodemon;--hot是热重载(尽量保留进程内状态),开发服务时更顺滑。bun build --target bun打出专门给 Bun 运行的产物,还可以用--target browser(浏览器)或--target node(Node)。type: "module"让项目默认使用 ESM 语法。bun run dev会读取 scripts 并用 Bun 执行;npm 生态的脚本大多原样可用,pre/post钩子同样生效。
5. bunx:临时执行 npm 命令
bunx create-vite my-app # 不全局安装,直接跑 npm 包的命令
bunx tsc --version
讲解:
bunx对标npx:临时下载并执行包里的命令,用完即走,不污染全局。- 首次执行会下载包(进全局缓存),第二次几乎瞬时——缓存复用与
bun install同源。
6. 常见陷阱
bun test找不到测试:默认匹配*.test.{ts,js,...}与*_test.*;文件名写成tests.ts不会被发现。- 在测试里读环境变量拿不到:
bun test有自己的环境隔离习惯,.env文件里的变量需要显式加载(Bun 自动读取项目根目录.env,但 CI 注入的变量名要与代码一致);CI 与本地差异优先排查变量来源。 - 依赖的 postinstall 脚本没执行:Bun 默认不运行依赖的生命周期脚本(供应链防护)。确需允许时把包名加进 package.json 的
trustedDependencies,不要图省事全局放开。 - 把
bun.lockb(旧二进制)留在仓库:1.2 之后新锁文件是bun.lock;两者并存会让团队成员解析出不同结果,迁移时删旧留新。 - 期待
bun run代替构建:bun run是”跑”,bun build才是”打包”;部署前忘了 build,服务器上跑的是旧产物。
7. 动手试试
- 给
factorial加1 的阶乘测试并运行bun test。 - 用
bun add安装zod,写一个校验邮箱的小函数与对应测试。 - 用
bun build打包,观察产物文件与体积;再试--target browser对比产物差异。 - 把上面四步写成 package.json 的 scripts,体验”一个文件就是项目说明书”。
8. 本篇小结
初学者要点:
- Bun 不改变 Node 的项目形态,只把安装、运行、测试、打包四件事变快变短;测试从第一天就写,成本几乎为零。
- 命令对照记忆:
npm install->bun install、npx->bunx、nodemon->bun --watch、jest->bun test。 - 文本锁文件
bun.lock入库,团队与 CI 版本一致。
进阶注意:
- 生命周期脚本默认禁用是安全特性不是 bug;
trustedDependencies是唯一的正规入口。 - 从 Jest 迁移先改 import、再跑全量;mock 与快照的深度用法留在测试篇专门练。