Bun v1.4.4 实测:2,025 个文件,bun fmt 2.6 秒跑完

5 阅读5分钟

先给结论:Bun v1.4.4把 bun fmt、bun check、bun install、Bun.SQL 全塞进了一个补丁版本。我在自己机器上实测了前三项,官方基准一并列在下面。

bun fmt:286 倍是怎么来的

Bun 官方在 Prettier 自己的仓库(2,616 个文件)上跑了一次对比:

工具耗时峰值内存
bun fmt59.7 ms48 MB
Prettier 3.9.1017.1 s459 MB

在 vitest、date-fns、monkeytype、h3 四个项目上,是 Prettier 3.9.9 的 234 到 425 倍。跟 oxfmt 0.74.0 比,fumadocs(2,452 个文件)快 31 倍,vite 快 6.2 倍,RSSHub 快 3.0 倍。

我的实测(Windows,Bun 1.4.4):date-fns main 分支 2,025 个 TS/JS 文件,第一遍 2,643 ms,第二遍 916 ms,20 线程。time 显示 user 只有 0.077 秒——时间花在线程和 IO 上。

迁移只差三条命令

bun fmt --doctor 先看兼容性。我搭了个带 .prettierrc 的假项目,终端原话:

Using .prettierrc (Prettier config)
You can remove: @trivago/prettier-plugin-sort-imports, prettier,
prettier-plugin-tailwindcss

挂 CI 用 bun fmt --check(有待格式化文件时退出码 1)。换钩子用 --staged:

#!/bin/sh
bun fmt --staged

受控实验:三行烂格式代码,21.43 毫秒改完,输出与 Prettier 惯例一致。

兼容底线:Prettier 测试套件 9,731 个用例过 9,648 个(99.1%);330 个开源仓库跟 oxfmt 输出一致率 94.6%。有 4 处故意不同——比如 CSS font-family: Font\ Awesome,Prettier 3.9.9 会输出 Font\Awesome(\A 被当成十六进制转义),bun fmt 原样保留。

bun check:省的不只是时间

项目v1.4.4v1.4.3
MUI docs1.31 s / 1.3 GB10.28 s / 11.2 GB
Supabase studio1.27 s2.84 s
VS Code2.82 s4.09 s

monorepo 用 bun check -b:4,000 个独立项目 13.65 s → 0.87 s(15.7x),峰值内存 1,987 MB → 872 MB。我的小项目实测:2 个文件 31.41 ms 抓到 TS2345,还提示我装 @types/bun。

bun install:两条救命改动

指数退避重试(最多 5 次,50/100/200/400/800 ms ±20% 抖动),注册表持续 503 一秒也能装上;Yarn Berry lockfile 可直接迁移(59.59 ms)。这版共修了 133 个 install issue。

其余值得记的:bun install --libc musl 让 Alpine 镜像不再装错 glibc 原生包;peer 依赖解析改得像 pnpm,@modelcontextprotocol/sdk 和 @remix-run/express 抢 express 不再靠猜;minimumReleaseAge = "3d" 支持单位(90m / 12 hours / 2 weeks),能挡刚发布就投毒的版本;packageExtensions 给漏声明依赖的包打补丁;bun pm cache pack/unpack 把缓存打成单文件在 CI 机器间搬运;bun publish --recursive 一条命令发完 monorepo,被依赖者先发。

Bun.SQL:每查省 48% CPU

PostgreSQL 客户端用户态指令:Point SELECT -48%、100 行 SELECT -30%、INSERT -21%、批量插入 1,000 行 × 10 列 -38%。query.cancel() 能掐掉跑飞的查询,COPY 流 1,000 MiB 以 255 MiB/s 过一遍、峰值 RSS 59 MiB。

bun:sqlite 的用户定义函数我在内存库上跑通了:

db.function("add2", (a, b) => a + b);
db.query("SELECT add2(12, 4) AS sum").get(); // { sum: 16 }

db.aggregate("add_all", {
  start: 0,
  step: (total, value) => total + value,
});
db.query("SELECT add_all(dollars) AS total FROM expenses").get();

升不升

bun fmt 今晚就能换(--doctor 兜底 + 配置全兼容);bun check 值得试(先跟 tsc 对输出);Bun.SQL 新能力先观望(要有真实慢查询场景再迁)。

两个我没想明白的:官方基准全在 Linux/macOS,我 Windows 上的 2,643 ms 没法直接比;bun fmt 是 oxc_formatter 的移植、后者是 Biome formatter 的移植,两层移植靠 99.1% 通过率兜底,那 0.9% 落在你项目里就是你的问题。

Bun v1.4.4 迁移 checklist + 实测脚本包

bun fmt 迁移五步(今晚就能做)

  1. 看兼容性:项目根目录跑 bun fmt --doctor。 它会告诉你读的是哪份配置(.prettierrc / .oxfmtrc.json / .editorconfig), 以及哪些包可以卸载(prettier、lint-staged、各类 sort-imports 插件)。
  2. 挂 CI:先加 bun fmt --check(列出待格式化文件,退出码 1),全绿之前别改默认命令。
  3. 换钩子:bun fmt --staged 格式化暂存区并重新 stage,替掉 lint-staged:
    # .husky/pre-commit
    #!/bin/sh
    bun fmt --staged
    
  4. 只查改动:bun fmt --changed(或 --changed=origin/dev)格式化与默认分支有差异的文件。
  5. 卸载:确认一周无回退后,把 prettier 及相关插件从 devDependencies 移除。

bunfig.toml 模板([fmt] 段)

[fmt]
printWidth = 100
semi = false
sortImports = true        # 内置 import 排序,可卸掉 sort-imports 插件
sortTailwindcss = true    # 内置 Tailwind class 排序
sortPackageJson = true    # 内置 package.json 键排序

[[fmt.overrides]]
files = ["*.test.ts"]
options = { printWidth = 120 }

bun install 退避重试(CI 抗注册表抽风)

# 默认:最多重试 5 次,间隔 50/100/200/400/800ms ±20% jitter
bun install

# 自定义初始延迟(ms):250 → 500 → 1000 → 2000 → 4000
BUN_CONFIG_HTTP_RETRY_BACKOFF=250 bun install

# Docker 分层:只下载依赖,不创建 node_modules
COPY package.json bun.lock ./
RUN bun pm fetch
COPY . .
RUN bun install --offline --frozen-lockfile

三档升级建议

档位对象理由
今晚就能换bun fmt--doctor 兜底 + 配置全兼容 + --check 可挂 CI
值得试bun check大型项目 1.4–7.8x,内存大降;先与 tsc 对输出
先观望Bun.SQL COPY 流 / query.cancel()功能真实,但要有真实慢查询场景再迁

测试粒子

//粒子一: sqlite-demo.ts

// Bun v1.4.4 新特性实测:bun:sqlite 用户定义函数 + 更新钩子
import { Database } from "bun:sqlite";

const db = new Database(":memory:");
db.run("CREATE TABLE expenses (dollars INTEGER)");

// 自定义标量函数
db.function("add2", (a: number, b: number) => a + b);
console.log("add2(12, 4) =", db.query("SELECT add2(12, 4) AS sum").get());

// 自定义聚合函数
db.aggregate("add_all", {
  start: 0,
  step: (total: number, value: number) => total + value,
});
db.run("INSERT INTO expenses VALUES (10), (20), (5)");
console.log("add_all =", db.query("SELECT add_all(dollars) AS total FROM expenses").get());

// 更新钩子
db.onInsert = (_db, table, rowid) => console.log(`inserted ${table} ${rowid}`);
db.run("INSERT INTO expenses (dollars) VALUES (100)");



// 粒子二:glob-demo.ts
const cases: [string, string][] = [
  ["**/!(*.test).ts", "src/cart.ts"],
  ["**/!(*.test).ts", "src/cart.test.ts"],
  ["src/@(app|lib)/**/*.ts", "src/app/a/b.ts"],
  ["shard-{1..3}.json", "shard-2.json"],
  ["shard-{1..9..2}.json", "shard-5.json"],
];
for (const [pattern, path] of cases) {
  console.log(
    `${pattern.padEnd(26)} vs ${path.padEnd(20)} => ${new Bun.Glob(pattern).match(path)}`,
  );
}


原创技术博客 · 开源项目分享 · AI全栈创作社区 idao.fun