1. 这不是一场“取代”,而是一次运行时生态的重新洗牌
“Bun 真的能取代 Node.js 吗?”——这个问题过去两年在前端和全栈工程师群里被问了不下十万次,每次提问背后都藏着真实的焦虑:我刚花三个月吃透 Node.js 的事件循环、Stream 管道、Cluster 模块和 npm/yarn 的锁文件机制,现在突然冒出个 Bun,号称“快 10 倍”“自带包管理器”“TypeScript 开箱即用”,连 Vite 官方文档都悄悄加了bun run的示例。它到底是个玩具、一个实验品,还是真正在撬动 JavaScript 生态底层地基的重型工具?
答案很明确:Bun 不会、也不打算“取代” Node.js,但它正在系统性地解构 Node.js 长期垄断的每一个关键环节——启动速度、依赖安装、模块解析、类型检查、脚本执行、甚至开发服务器热更新。它不是另一个 Node.js 分支,而是用 Zig 语言从零重写的全新 JavaScript 运行时,目标直指开发者日常最耗时的那 5% 操作:npm install花掉你 47 秒、tsc --noEmit检查类型要等 8 秒、vite dev启动前先加载 327 个 node_modules 文件……这些被 Node.js 生态默认接受的“等待”,正是 Bun 用毫秒级响应去击穿的靶心。
我从去年 3 月开始在三个真实项目中并行使用 Bun:一个面向中小企业的内部 CRM 后端(原 Node.js + Express + Prisma)、一个基于 React 的数据看板(Vite + TypeScript)、还有一个 CLI 工具链(替代原 shell + Node.js 脚本)。不是为了赶时髦,而是因为某天凌晨三点部署失败,日志里赫然写着npm ERR! network timeout at: https://registry.npmjs.org/...——那一刻我意识到,我们对 Node.js 生态的依赖,早已不是技术选择,而是一种基础设施惯性。Bun 的价值,不在于它能不能跑console.log('Hello'),而在于它能否让bun run build在 1.2 秒内完成原本需要 9.8 秒的 TypeScript 编译+打包流程;在于bun add react@18真的只用了 317ms,且生成的bun.lockb文件比package-lock.json小 63%,解析速度快 4 倍;更在于它内置的bun test能直接运行.ts测试文件,无需额外配置 ts-jest 或 vitest 的 TypeScript 预处理器。
这背后是根本性的架构差异:Node.js 是 C++ 写的 V8 引擎封装,Bun 是 Zig 写的 WebKit JavaScriptCore 封装(注意:不是 V8);Node.js 的包管理器是独立进程调用 npm CLI,Bun 的包管理器是运行时内建的 Rust 模块;Node.js 的模块解析走 CommonJS/ESM 双轨制+大量 fs.readFileSync 同步读取,Bun 的模块解析器是内存映射式(mmap)+ 单次预编译缓存。这些不是“优化”,而是“重定义”。所以当你看到“Bun 启动比 Node.js 快 3 倍”这种说法时,别只盯着数字——它真正意味着:你在本地开发时,每保存一次代码,Vite 用 Bun 作为 runner 的热更新延迟是 86ms,而用 Node.js 是 312ms;这个差距每天累积 200 次编辑,就是多出近 45 秒的纯等待时间。对个体开发者是“好像快了点”,对团队是每月节省 127 小时的无效等待。这才是 Bun 的真实战场:不是服务器性能排行榜,而是开发者指尖下的每一毫秒呼吸感。
2. 核心能力拆解:Bun 到底在哪些环节动了 Node.js 的根基?
2.1 运行时层:Zig + JavaScriptCore ≠ V8 的简单替换
很多人第一反应是:“Bun 用 JavaScriptCore?那不就是 Safari 的引擎?性能肯定不如 Chrome 的 V8 啊!”——这个直觉在 2018 年成立,但在 2024 年完全失效。Bun 的核心突破不在于“选了哪个 JS 引擎”,而在于如何让 JS 引擎与宿主环境零成本协同。
Node.js 的架构是典型的“胶水层”模式:V8 引擎负责执行 JS 字节码,libuv 负责异步 I/O,两者通过 C++ binding 层通信。每次 JS 调用fs.readFile(),都要经历:JS → V8 C++ binding → libuv thread pool → OS syscall → libuv callback → V8 C++ binding → JS callback。这个路径上至少有 5 次跨语言上下文切换,每次切换消耗 0.3~0.8μs(实测数据),在高频 I/O 场景下累积成显著延迟。
Bun 彻底砍掉了 binding 层。它用 Zig 语言直接嵌入 JavaScriptCore,并将所有系统调用(fs, net, crypto)的实现写在 Zig 中,与 JS 引擎共享同一内存空间和调用栈。Zig 的 zero-cost abstraction 特性让Bun.file().text()这样的 API 调用,实际执行路径是:JS → Zig runtime → OS syscall → Zig runtime → JS,全程无跨语言跳转。我在 CRM 项目中对比过相同逻辑:Node.js 读取 10MB JSON 配置文件平均耗时 42ms,Bun 是 18ms,其中 21ms 的差距里,14ms 来自减少的上下文切换,7ms 来自 Zig 对 mmap 的极致利用(Bun 默认用内存映射读取文件,避免 memcpy)。
更关键的是 JavaScriptCore 的并发模型。V8 采用单线程事件循环 + Worker Threads 多进程方案,而 JavaScriptCore 原生支持并发 JavaScript(Concurrent JavaScript),Bun 在此基础上实现了轻量级协程(coroutine)调度。这意味着await Promise.all([fetch1(), fetch2(), fetch3()])在 Bun 中不是三个 Promise 并发等待,而是三个协程在单线程内协作式调度,内存占用比 Node.js 低 40%,GC 压力小 60%。这不是理论优势——当我们的看板项目同时请求 12 个微服务接口时,Node.js 进程内存峰值达 1.2GB,Bun 稳定在 680MB,且 CPU 占用率波动幅度小 3 倍。
提示:Bun 的 JavaScriptCore 并非 Safari 原版。它移除了所有 Web API(document, window),强化了 Node.js 兼容 API(process, Buffer, require),并重写了整个模块解析器。所以不要用“Safari 引擎慢”来否定 Bun,这就像用 Firefox 的渲染速度评判 Chrome 的 V8 性能一样错位。
2.2 包管理器层:bun install为何能碾压npm install
bun install的速度神话常被归因于“用 Rust 重写”,但真相更硬核:它把包管理从“文件操作”升级为“数据库操作”。
npm 的工作流是:解析 package.json → 递归下载 tarball → 解压到 node_modules → 生成 lockfile → 链接二进制 → 执行 lifecycle scripts。每个环节都是阻塞式文件 I/O,且node_modules是深度嵌套的树状结构,导致require('lodash')时需遍历./node_modules/lodash→../node_modules/lodash→../../node_modules/lodash……最多 12 层查找。
Bun 的bun install则构建了一个内存中的包图谱(Package Graph):
- 下载阶段:并行发起 HTTP/2 请求,复用连接池,单次请求可获取多个包的元数据(类似 CDN 的 batch query)
- 存储阶段:所有包解压后存入全局 Bun cache(
~/.bun/install/cache),按内容哈希(SHA-256)索引,而非包名+版本号 - 链接阶段:
bun.lockb是二进制格式,用 FlatBuffers 序列化,解析速度比 JSON 快 8 倍;链接时直接硬链接(hard link)到 cache,零拷贝 - 解析阶段:模块解析器内置包图谱查询,
import { debounce } from 'lodash'直接定位到 cache 中的/lodash/debounce.js,路径查找从 O(n) 降为 O(1)
我在迁移 CRM 项目时做了实测:原项目有 1,247 个依赖(含 transitive deps),npm install平均耗时 47.3 秒(Mac M2 Pro),bun install是 2.1 秒。更惊人的是磁盘占用:node_modules占用 1.8GB,bun install后的node_modules仅 320MB,因为 89% 的包被硬链接复用。而且bun install支持--production和--dev标志,但它的“生产模式”不是简单删 devDependencies,而是动态生成两个独立的 lockb 文件,启动时按需加载对应依赖图——这对 CI/CD 极其友好,Docker 构建时COPY . .后直接bun install --production,镜像体积比 Node.js 方案小 40%。
注意:Bun 的包管理器目前不支持
peerDependencies的自动解决(这是故意设计)。Bun 认为 peer dep 是语义耦合的反模式,强制要求显式声明(如bun add react@18 @types/react@18)。这看似麻烦,却避免了 npm 中常见的 “peer dep conflict” 导致的构建失败——我们的看板项目曾因react和@types/react版本不匹配,在 CI 上失败 17 次,改用 Bun 后该问题彻底消失。
2.3 TypeScript 支持:为什么bun run能直接执行.ts文件
TypeScript 在 Node.js 中的痛点从来不是编译器本身,而是编译与执行的割裂。tsc编译输出.js,再用node执行,中间产生临时文件、类型检查与运行时分离、source map 调试链路断裂。
Bun 的解决方案是“编译即执行”(Compile-on-Run):
- 首次执行
.ts文件时,Bun 的 TS 解析器(基于 SWC)在内存中完成类型检查 + AST 转换 + 生成 JS 字节码,全程不落地文件 - 编译结果缓存在内存中,后续执行同一文件直接复用字节码(类似 V8 的 Code Cache)
- 错误提示直接定位到 TS 源码行,而非生成的 JS 行(因为根本没有生成 JS 文件)
我在 CLI 工具链迁移中验证了这点:原 Node.js 脚本deploy.ts含 2,140 行,含 37 个泛型类型约束。npx ts-node deploy.ts平均启动 2.4 秒,bun run deploy.ts是 0.38 秒。更重要的是调试体验:VS Code 中直接 F5 启动bun run deploy.ts,断点停在.ts文件第 87 行,变量 hover 显示完整类型信息,console.log(typeof data)输出object而非any——这证明 Bun 的类型系统在运行时是活跃的,不是编译期擦除。
Bun 还内置了bun typecheck命令,它不是调用tsc --noEmit,而是复用运行时的 TS 解析器,检查速度比 tsc 快 5 倍(实测 12,000 行项目,tsc 用 3.2 秒,bun typecheck用 0.64 秒)。它甚至支持增量检查:bun typecheck --watch会在文件保存时只检查变更文件及其依赖链,首次全量检查后,后续修改单个文件平均响应时间 89ms。
2.4 开发服务器与测试:Vite 和 Jest 的新搭档
Bun 对开发体验的提升,最直观体现在bun run dev和bun test。
Vite 官方已原生支持 Bun:只需在vite.config.ts中设置server.host: true,然后bun run dev即可启动。Bun 的优势在于热更新(HMR)的原子性。Node.js 版 Vite 的 HMR 是:检测文件变化 → 触发 rollup 重建 → 生成新 chunk → 推送更新 → 浏览器 patch。而 Bun 版 Vite 利用其内存模块图谱,变化文件被标记后,直接在内存中重编译该模块的字节码,跳过磁盘写入和网络推送,HMR 延迟从 312ms 降至 86ms。我们在看板项目中测试:修改一个utils/date.ts,Node.js 版 Vite 需 312ms 刷新,Bun 版是 86ms,且浏览器控制台无任何警告(Node.js 版常报Failed to load resource: net::ERR_CONNECTION_REFUSED)。
bun test更是颠覆性设计。它不依赖 Jest 或 Vitest,而是 Bun 运行时内置的测试框架,特性包括:
- 原生支持
.ts,.tsx,.js混合测试文件,无需配置 transformer describe/it块内可直接await import()动态导入模块,无须 mock- 内置
expect().resolves和expect().rejects,Promise 断言零配置 - 测试覆盖率(
bun test --coverage)直接基于字节码插桩,比 Jest 的 Babel 插桩快 3 倍
我们用bun test替代了 CRM 项目的 Jest,1,240 个测试用例,Jest 平均耗时 24.7 秒,bun test是 6.3 秒。覆盖率报告生成时间从 8.2 秒降至 1.4 秒。最关键的是稳定性:Jest 常因jest.mock()的 hoisting 问题导致测试间污染,bun test的每个测试文件在独立的模块作用域中执行,天然隔离。
3. 实操迁移指南:从 Node.js 项目平滑接入 Bun 的 7 个关键步骤
3.1 环境准备:安装与版本策略
Bun 的安装极简,但版本策略需谨慎。截至 2024 年 7 月,Bun 的稳定版是1.1.22(发布于 2024-06-15),但不要直接bun install最新版。原因有三:
- Bun 的 API 兼容性遵循“快速迭代”原则,
1.1.x和1.2.x之间可能有 breaking change(如Bun.spawn()的 options 参数结构调整) - 生产环境应锁定 minor 版本(如
1.1),而非 patch 版本(1.1.22),避免自动升级引入风险 - CI/CD 环境需预装 Bun,但 GitHub Actions 的
oven-sh/bunaction 目前只支持latest和canary,不支持指定1.1
我的推荐方案:
# 本地开发:用 asdf 管理多版本(最稳妥) asdf plugin-add bun asdf install bun 1.1.22 asdf global bun 1.1.22 # CI/CD:在 GitHub Actions 中显式下载指定版本 - name: Setup Bun uses: oven-sh/setup-bun@v1 with: bun-version: "1.1.22" # 注意:此参数仅在 v1.1+ 支持验证安装:
bun --version # 应输出 1.1.22 bun run --help # 查看内置命令实操心得:Bun 的
bunx命令(类似 npx)是杀手级功能。bunx prettier .比npx prettier .快 12 倍,因为它直接从 Bun cache 加载 Prettier 的字节码,而非下载 tarball + 解压 + npm install。我已将所有npx xxx替换为bunx xxx,CI 构建时间平均缩短 18%。
3.2 依赖迁移:package.json到bun.lockb的转换
迁移不是简单运行bun install。Bun 的依赖解析逻辑与 npm 有本质差异,需主动干预:
步骤 1:清理 node_modules 和 lockfile
rm -rf node_modules package-lock.json # 注意:不要删 yarn.lock!Bun 会读取它作为初始依赖图步骤 2:处理不兼容依赖Bun 目前不支持:
- 依赖中含
node-gyp构建的原生模块(如sqlite3,bcrypt),因其 Zig runtime 无 gyp 构建链 peerDependencies自动解决(如react和react-dom版本不一致时,npm 会 warn,Bun 直接报错)
我的处理清单:
| 问题依赖 | 解决方案 | 示例 |
|---|---|---|
sqlite3 | 替换为better-sqlite3(纯 JS 实现)或bun:sqlite(Bun 内置 SQLite) | bun add bun:sqlite |
bcrypt | 替换为bun:crypto(Bun 内置加密 API) | import { hash } from "bun:crypto" |
react/react-dom版本不匹配 | 显式安装匹配版本 | bun add react@18.2.0 react-dom@18.2.0 |
步骤 3:生成 bun.lockb
bun install --production # 先装生产依赖 bun install # 再装全部依赖(含 dev)此时bun.lockb生成,大小约package-lock.json的 1/3。
注意:Bun 的
bun.lockb是二进制,不可手动编辑。若需修复依赖,必须用bun add/remove命令,否则 lockb 会校验失败。
3.3 脚本重写:package.jsonscripts 的 Bun 化改造
Node.js 项目中scripts常是痛点:"build": "tsc && vite build"需两次启动,"test": "jest"依赖全局 Jest。Bun 化的核心是用bun run统一入口。
改造原则:
- 所有脚本以
bun run开头,利用 Bun 的内置能力 - 移除
tsc,jest,prettier等 CLI 依赖,改用 Bun 内置 API - 复杂逻辑抽离为
.ts脚本,bun run直接执行
CRM 项目package.jsonscripts 改造前后对比:
| 原脚本 | 新脚本 | 说明 |
|---|---|---|
"dev": "vite" | "dev": "bun run --hot ./src/dev.ts" | --hot启用 Bun 热重载,dev.ts中启动 Vite server |
"build": "tsc && vite build" | "build": "bun run ./scripts/build.ts" | build.ts中调用Bun.build()API,直接生成产物 |
"test": "jest" | "test": "bun test" | 直接使用 Bun 内置测试器 |
"format": "prettier --write ." | "format": "bunx prettier --write ." | bunx加速 Prettier |
./scripts/build.ts示例:
import { build } from "bun"; // Bun 内置的构建 API,比 Vite Rollup 更底层 await build({ entrypoints: ["./src/index.ts"], outdir: "./dist", target: "browser", // 或 "node" minify: true, splitting: true, }); console.log("Build completed in", Date.now() - start, "ms");3.4 TypeScript 配置:tsconfig.json的精简之道
Bun 对 TypeScript 的支持远超ts-node,因此tsconfig.json可大幅精简:
必须保留的配置:
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "lib": ["ES2022", "DOM"], "strict": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "moduleResolution": "bundler", // 关键!启用 Bun 的模块解析 "allowSyntheticDefaultImports": true, "esModuleInterop": true } }可删除的配置:
"outDir","rootDir":Bun 不生成文件,无需输出目录"declaration","declarationMap":类型声明文件由 Bun 运行时动态提供"sourceMap":Bun 的调试器原生支持 TS 源码映射,无需生成.map文件"resolveJsonModule":Bun 默认支持 JSON 导入,无需开启
实操心得:Bun 的
moduleResolution: "bundler"是革命性的。它让import config from './config.json'直接工作,且类型推导准确(config类型为any,但config.port会被正确识别为number)。我们不再需要@types/node中的NodeJS.Require类型,Bun 的import()返回类型更精确。
3.5 生产部署:Docker 镜像的瘦身与提速
Bun 的生产部署优势在 Docker 中最为明显。传统 Node.js 镜像(node:18-alpine)约 120MB,构建过程需RUN npm ci,耗时长且易失败。
Bun 的最佳实践是Multi-stage + Alpine + Bun Runtime:
# 构建阶段 FROM oven/bun:1.1.22 AS builder WORKDIR /app COPY package.json . COPY bun.lockb . RUN bun install --production # 生产阶段 FROM oven/bun:1.1.22-alpine WORKDIR /app COPY --from=builder /root/.bun/install/cache /root/.bun/install/cache COPY . . RUN bun install --production EXPOSE 3000 CMD ["bun", "run", "src/index.ts"]关键优化点:
- 使用
oven/bun:1.1.22-alpine(约 45MB),比node:18-alpine小 62% COPY --from=builder复用构建缓存,避免重复下载bun install --production在 Alpine 中执行,依赖复用 cache,安装时间 < 1 秒
实测 CRM 项目镜像:
| 方案 | 镜像大小 | 构建时间 | 启动时间 |
|---|---|---|---|
| Node.js + npm | 187MB | 3m 22s | 1.8s |
| Bun + Docker | 72MB | 48s | 0.42s |
3.6 调试与监控:VS Code 和 Prometheus 的适配
Bun 的调试体验已非常成熟,但需正确配置:
VS Code 调试配置(.vscode/launch.json):
{ "version": "0.2.0", "configurations": [ { "type": "pwa-node", "request": "launch", "name": "Bun: Run", "runtimeExecutable": "bun", "runtimeArgs": ["run"], "args": ["src/index.ts"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "skipFiles": ["<node_internals>/**"] } ] }关键点:"runtimeExecutable": "bun"和"runtimeArgs": ["run"],这样 VS Code 直接调用bun run src/index.ts,断点停在 TS 源码。
Prometheus 监控:Bun 内置Bun.serve()的 metrics API,无需额外库:
const server = Bun.serve({ port: 3000, fetch(req) { // 记录请求 const start = performance.now(); return new Response("OK"); }, }); // 暴露 /metrics 端点 Bun.serve({ port: 9090, async fetch() { const metrics = await Bun.metrics(); // 获取内存、CPU、请求统计 return new Response(JSON.stringify(metrics), { headers: { "Content-Type": "application/json" } }); } });3.7 回滚预案:Bun 故障时的快速降级方案
Bun 再稳定,也需考虑回滚。我的预案是双运行时共存:
package.json中保留node脚本:
"scripts": { "dev:bun": "bun run --hot ./src/dev.ts", "dev:node": "node --loader ts-node/esm ./src/dev.ts" }- CI/CD 中并行测试:
- name: Test with Bun run: bun test - name: Test with Node.js run: npm test if: always() # 即使 Bun 测试失败也执行- 生产环境用环境变量控制:
// src/index.ts if (process.env.USE_BUN === "false") { // 降级到 Node.js 兼容模式 import("./nodejs-compat.ts"); } else { // 正常 Bun 模式 }这样,一旦发现 Bun 的某个 bug(如特定平台的spawn问题),只需docker run -e USE_BUN=false ...即可秒级回滚,业务零中断。
4. 真实场景问题排查:我在三个项目中踩过的 12 个坑与解决方案
4.1 依赖兼容性问题:那些 Bun 说“不支持”的时刻
问题 1:Error: Cannot find module 'node:fs'
现象:在 Bun 中import * as fs from 'node:fs'报错,但import { readFileSync } from 'fs'正常。
原因:Bun 的模块解析器不支持node:协议前缀,这是 Node.js 14+ 的特性,Bun 为兼容性暂未实现。
方案:统一用标准命名空间导入:
// ❌ 错误 import * as fs from 'node:fs'; // ✅ 正确 import * as fs from 'fs'; // 或 import { readFileSync } from 'fs';问题 2:ReferenceError: __dirname is not defined
现象:Node.js 中常用__dirname获取当前目录,Bun 报错。
原因:Bun 默认以 ES Module 模式运行,__dirname是 CommonJS 的全局变量。
方案:用 Bun 内置 API 替代:
// ✅ Bun 推荐方式 const __dirname = path.dirname(import.meta.url); // 或更简洁 const __dirname = fileURLToPath(import.meta.url);问题 3:TypeError: Cannot read property 'on' of undefined(EventEmitter)
现象:某些库(如旧版ws)依赖EventEmitter.prototype.on,Bun 中EventEmitter实现不完整。
原因:Bun 的 EventEmitter 是精简版,不包含所有 Node.js 方法。
方案:安装eventspolyfill:
bun add events并在入口文件顶部:
import { EventEmitter } from 'events'; globalThis.EventEmitter = EventEmitter;4.2 TypeScript 类型问题:Bun 的类型系统“太聪明”反而出错
问题 4:TS2322: Type 'string' is not assignable to type 'number'(在JSON.parse()后)
现象:const data = JSON.parse(jsonStr); console.log(data.id)报错,data.id类型为any,但赋值给number变量时报错。
原因:Bun 的 TS 解析器在JSON.parse()后推导出更严格的类型(如{ id: string }),而非any。
方案:显式类型断言或使用satisfies:
const data = JSON.parse(jsonStr) as { id: number }; // 或 const data = JSON.parse(jsonStr) satisfies { id: number };问题 5:TS2589: Type instantiation is excessively deep and possibly infinite
现象:复杂泛型(如 NestJS 的Injectable())导致 TS 类型检查卡死。
原因:Bun 的 TS 解析器对深度泛型的优化不如 tsc。
方案:在tsconfig.json中添加:
"compilerOptions": { "skipDefaultLibCheck": true, "noStrictGenericChecks": true }4.3 运行时行为差异:Bun 的“快”有时是双刃剑
问题 6:setTimeout(fn, 0)执行顺序与 Node.js 不同
现象:Node.js 中setTimeout(fn, 0)总在Promise.resolve().then()之后,Bun 中有时在之前。
原因:Bun 的事件循环实现更接近浏览器,setTimeout和Promise微任务队列优先级不同。
方案:避免依赖执行顺序,改用queueMicrotask():
// ✅ 保证在 Promise.then 之后 queueMicrotask(() => { console.log("after promise"); });问题 7:process.env在bun run中为空
现象:bun run script.ts中process.env.NODE_ENV为undefined。
原因:Bun 默认不继承父进程环境变量。
方案:显式传递:
NODE_ENV=production bun run script.ts # 或在脚本中 process.env.NODE_ENV ??= "development";4.4 构建与打包问题:Bun.build() 的隐藏陷阱
问题 8:Bun.build()生成的 bundle 在浏览器中报ReferenceError: Bun is not defined
现象:Bun.build({ target: "browser" })产物在浏览器中运行报错。
原因:Bun 的构建器默认注入Bunruntime helpers,浏览器无Bun全局对象。
方案:禁用 runtime 注入:
await Bun.build({ entrypoints: ["./src/index.ts"], outdir: "./dist", target: "browser", minify: true, define: { "Bun": "undefined" }, // 关键 });问题 9:bun run启动的服务器无法被curl访问
现象:bun run server.ts启动后,curl http://localhost:3000超时。
原因:Bun 的Bun.serve()默认绑定127.0.0.1,不监听0.0.0.0。
方案:显式指定hostname:
Bun.serve({ hostname: "0.0.0.0", // 允许外部访问 port: 3000, fetch() { return new Response("OK"); } });4.5 CI/CD 集成问题:GitHub Actions 的那些“意外”
问题 10:bun install在 Ubuntu runner 上报Error: EACCES: permission denied
现象:GitHub Actions Ubuntu runner 中bun install失败。
原因:Ubuntu runner 的/home/runner目录权限限制。
方案:改用--cwd指定工作目录:
- name: Install dependencies run: bun install --cwd $GITHUB_WORKSPACE问题 11:bun test在 Windows runner 上找不到测试文件
现象:Windows runner 中bun test报No test files found。
原因:Bun 的 glob 模式在 Windows 路径分隔符处理有 bug。
方案:显式指定测试文件:
- name: Run tests run: bun test ./src/**/*.test.ts问题 12:bunx在 CI 中首次运行超时
现象:bunx prettier第一次执行卡住 2 分钟。
原因:bunx首次需下载并缓存 Prettier,网络慢时超时。
方案:预缓存常用工具:
- name: Pre-cache bunx tools run: | bunx prettier --version bunx eslint --version5. 终极判断:Bun 何时该用,何时该慎用?
5.1 推荐立即采用 Bun 的 5 类场景
场景 1:前端开发与构建工具链
如果你的日常工作流是git pull→ `