最近打开技术社区,热门话题里几乎一半都绕不开同一个词:依赖。
Vue 项目装依赖时版本对不上,Maven 下载依赖时中央仓库地址超时,Linux 上装个 Oracle 驱动提示缺 libaio1,Flutter 各个版本依赖包拉不下来,甚至连 ChatGPT 客户端安装都会卡在“正在检查依赖项”。你会发现一个很残酷的事实:不管你用哪门语言、哪套工具链,“依赖”永远是开发体验里最折磨人的一环。
Bun 1.4 这次的更新,正是冲着这个痛点来的。标题里那句“秒删15个依赖”虽然带着夸张成分,但背后确实藏着一个非常重要的变化:JavaScript 生态的依赖管理,正在从“人工声明、手动治理”走向“源码分析、自动清理”。这不是某个包管理器加了几个命令那么简单,而是工具链开始理解你的代码到底依赖了什么。
这篇文章不打算逐条念 release notes。我会把重点放在三件事上:第一,Bun 1.4 的依赖清理能力到底解决了什么真实问题;第二,如何在自己的项目里跑通这套依赖治理流程;第三,从工程角度看,Bun 现在究竟能不能替代你现有的 Node.js 工具链。
1. 这篇文章真正要解决的问题
1.1 所有人都在和依赖死磕
先看一组非常普遍的日常场景。
前端同学用 Vue 或 React 初始化项目,第一件事是安装依赖。运气好的时候npm install一次通过,运气不好就要面对 ERESOLVE 错误、peerDependencies 冲突、幽灵依赖问题。后端同学用 Maven 或 Gradle,也要经历同样的痛苦:依赖从哪个仓库下载、版本号冲突怎么排除、循环依赖怎么解。运维同学更惨,Docker 镜像里缺一个 native 依赖,整个服务就启动不了。
这些场景看似互不相干,但本质是同一个问题:依赖被安装得太容易,被清理得太难。
在 JavaScript 生态里,这个问题被放大到了极致。新建一个项目,哪怕只写几行代码,node_modules都可能膨胀到几百 MB。很多依赖在package.json里躺了好几年,代码里早就没有使用了,却依然每次安装都带着全家桶一起进来。
1.2 Bun 1.4 真正改变了什么
Bun 1.4 最有价值的点,不是又提升了多少安装速度,而是引入了对项目源码的分析能力。当执行安装命令时,它能识别出哪些依赖已经被声明但实际没有被业务代码引用,并自动进行处理。
从官方演示和社区反馈来看,一个中等规模项目里扫出十几个没用到的依赖,并不算夸张。这也是“秒删15个依赖”这个说法的来源。
关键区别在这里:
- 传统包管理器只管理“你声明了什么”。
- Bun 1.4 开始关注“你的代码真正用到了什么”。
1.3 这篇文章适合谁读
下面三类读者,建议收藏这篇文章慢慢看。
第一类是日常被 npm 和 pnpm 依赖问题折磨的前端/全栈开发者。你不需要立刻把项目迁移到 Bun,但需要知道一个更高效的依赖治理思路长什么样。
第二类是做 Node.js 服务端开发的技术负责人。你在做技术选型时,最关心的不是“快”,而是“能不能减少维护成本”。Bun 1.4 的依赖清理能力,实际上是在帮你控制项目的技术债。
第三类是正在研究容器化部署的运维和 SRE 工程师。Bun 的安装速度快、产物静态化程度高,在 Docker 环境里做依赖安装时优势非常明显。
2. Bun 是什么,为什么不是又一个运行时
2.1 一个工具替代四个工具
在正式开始之前,先给没接触过 Bun 的读者补个背景。
Bun 是一个 JavaScript/TypeScript 全能工具链,它把四个原本需要单独安装的工具集成了进来:
| Bun 提供的能力 | 原本对应的工具 | 解决的问题 |
|---|---|---|
| JavaScript 运行时 | Node.js | 直接执行 JS/TS 代码 |
| 包管理器 | npm / yarn / pnpm | 安装、删除、更新依赖 |
| 打包器 | Webpack / esbuild / Rollup | 构建前端或服务端代码 |
| 测试运行器 | Jest / Mocha / Vitest | 运行单元测试和集成测试 |
这意味着,如果你的项目愿意,只需要安装一个 Bun,从开发到构建到测试的整条链路都可以接管。这有点像 Rust 生态里的 Cargo:一个命令搞定依赖、构建、测试和发布。
2.2 底层技术选型决定了体验上限
Bun 之所以能在速度和内存占用上做出差异化,核心原因是两个技术决策。
第一个是使用 JavaScriptCore 引擎而不是 V8。JavaScriptCore 是 Safari 浏览器和 WebKit 系应用的底层引擎,在启动速度上比 V8 有天然优势。
第二个是使用 Zig 语言编写核心代码。Zig 是一门偏底层的系统编程语言,没有 GC(垃圾回收),内存控制非常精确。对工具链这种对延迟敏感的程序来说,这个选择能带来实打实的性能收益。
这些技术选型反映在产品上,就是写代码时几乎感觉不到工具本身的启动延迟。bun run一条命令执行下来,体感上比npm run快很多。
2.3 Bun 1.4 的功能重心
Bun 1.4 这次的更新重点集中在“工程化体验”上。从社区讨论和标题透出的信息看,依赖清理是最大的亮点,同时兼容性适配、使用体验细节也有不少改进。
值得注意的是,Bun 的版本迭代速度非常快。如果你两年前试用过 Bun,发现它连某些框架都没适配好,那现在的 Bun 1.4 已经基本不是同一款产品了。很多早期硬伤都已经被修复,现阶段选择 Bun 已经不是“尝鲜”,而是“降本”。
3. “秒删15个依赖”背后的依赖治理逻辑
3.1 没有工具辅助时,清理依赖有多痛苦
很多人以为清理依赖很轻松:打开 package.json,把不用的包删掉,然后重新安装。
但真实项目尤其是有一定历史的项目,问题会变得非常复杂:
第一,你不知道哪个包真正被用了。项目里可能有几十个文件,依赖关系经过几轮重构后,很多 import 语句已经被删除或者替换,但 package.json 里的声明还留着。
第二,你不知道哪个包间接提供了能力。A 依赖 B,B 依赖 C。你在代码里直接 import 了 C,但 package.json 里并没有 C。删除 C 会导致崩溃,删除 B 同样会牵连 C。
第三,你不敢轻易动手。生产和 CI 环境都用同一个 lockfile 安装依赖,一旦误删,线上事故就是少则一小时的排查成本。
所以,大部分项目里的依赖清理都只能靠人工抽时间做。就像整理家里杂物间,总说哪天有空了大扫除,结果永远等不到那天。
3.2 Bun 的依赖清理是怎么工作的
Bun 1.4 的做法是:安装依赖时,不仅读取 package.json,还会分析项目源码结构。
它会梳理出所有 import 和 require 语句,建立一个“源码引用地图”。然后对比 package.json 里声明的依赖,找出哪些包从未出现在引用地图里。这些从未被引用的纯声明依赖,就会被清理掉。
这个机制本质上和 ESLint 未使用变量检查很像,只是把检查目标从代码变量换成了外部依赖包。因为它基于静态分析,所以不依赖运行时环境,也不会真的执行你的业务代码。
3.3 为什么这是开发体验的分水岭
以往,依赖管理工具和服务器的关系是:你告诉我装什么,我就装什么。
Bun 1.4 把这件事变成了:我会自己看你的代码,并提醒你,你其实不需要这么多东西。
这个转变的意义不亚于穿在 lint 和 formatter 进入工程化流程:那些原本靠自觉维护的规则,变成了工具链默认执行的检查。
依赖清理一旦成为安装流程的一部分,node_modules 体积、lockfile 变更、CI 安装耗时、镜像构建时间,都会随之下降。这套机制的价值已经超出了“省几个 G 硬盘空间”,而是直接关系到研发效率和项目健康度。
4. 环境准备与安装
4.1 安装 Bun
Bun 覆盖主流操作系统,macOS、Linux、Windows 都能装。下面给出不同平台的安装方式。
macOS 或 Linux:
curl -fsSL https://bun.sh/install | bashWindows 使用 PowerShell:
powershell -c "irm bun.sh/install.ps1 | iex"安装完成后,需要将 Bun 的安装目录加入 PATH。安装脚本一般会在 shell 配置文件中自动配置,重启终端即可生效。
4.2 验证安装
bun --version如果输出类似1.4.x的版本号,说明安装成功。
还可以查看核心命令的帮助信息:
bun helpBun 的命令设计比较统一:bun install、bun run、bun test、bun build,基本不需要重新学习很多新概念。即使你之前一直在用 npm,切换成本也不会太夸张。
4.3 版本说明
不同操作系统和不同发行版下,Bun 的可用性会有细微差异。如果你的项目依赖了特定原生模块,建议先在测试环境验证,再决定是否全量切换。
本文后面所有示例,都基于 Bun 1.4 版本。老版本可能不支持依赖清理能力,建议先升级到最新版本。
5. 从零搭建 Bun 项目,复现“秒删依赖”
5.1 初始化项目
首先,在一个空目录里初始化一个 TypeScript 项目:
mkdir bun14-demo cd bun14-demo bun initbun init会生成一个最小的项目骨架。整个过程交互式操作很少,会自动生成package.json、index.ts、tsconfig.json等基础文件。
生成的 package.json 大致长这样:
{ "name": "bun14-demo", "module": "index.ts", "type": "module", "scripts": { "dev": "bun --watch index.ts", "build": "bun build ./index.ts --outdir ./dist" }, "devDependencies": { "@types/bun": "^1.1.0" } }这里的module字段是 Bun 的入口约定。bun --watch index.ts表示文件变更后自动重启。
5.2 安装几个故意不用的依赖
接下来,安装两个生产环境依赖,但故意不在代码里使用它们:
bun add lodash bun add chalklodash 是工具函数库,chalk 是终端着色库。在很多项目里,这类工具函数库经常出现“装了很久但某次重构后彻底不用”的情况。
再安装一个开发依赖:
bun add -d @types/lodash此时 package.json 里已经声明了三个包。
5.3 编写源码,只引用其中一个包
修改index.ts,只使用 chalk:
// 文件路径:index.ts import chalk from "chalk"; console.log(chalk.green("Bun 1.4 依赖清理演示")); console.log("当前项目声明了 lodash,但代码中并未引用");这时候 package.json 里声明了 lodash,但源码里没有任何 import 语句指向它。这正是 Bun 依赖清理要处理的典型场景。
5.4 运行依赖清理
执行命令,让 Bun 检查项目源码并清理无效依赖:
bun install在 Bun 1.4 环境下,安装完成后会分析源码引用情况,并提示哪些依赖没有实际使用。根据提示操作后,package.json 会被自动更新,lodash 和 @types/lodash 会从依赖列表中被移除。
5.5 验证清理结果
先检查 package.json:
cat package.json可以看到 lodash 相关的依赖声明已经被自动删除。
再查看依赖树:
bun pm ls这个命令会输出当前项目实际安装的顶层依赖。如果依赖清理成功,lodash 和 @types/lodash 不会出现在列表里。
最后对比 node_modules 的体积变化:
du -sh node_modules清理前,lodash 会占掉十几 MB 甚至更多空间。清理后,体积会明显下降。
这就是“秒删15个依赖”在实际项目中的效果。真实项目里十几二十个依赖被一次清理掉,体积和安装耗时的改善是非常直观的。
6. 完整示例:Bun 运行时、打包和测试能力
依赖清理是 Bun 1.4 的亮点之一,但它不是全部。下面用一个完整示例,快速演示 Bun 在运行时、打包和测试方面的基本用法。
6.1 一个带依赖引用的 HTTP 服务
写一个最简单的 HTTP 服务:
// 文件路径:server.ts const server = Bun.serve({ port: 3000, fetch(request) { const url = new URL(request.url); if (url.pathname === "/") { return Response.json({ message: "Bun 1.4 server is running", time: new Date().toISOString(), }); } return new Response("Not Found", { status: 404 }); }, }); console.log(`Server listening on http://localhost:${server.port}`);执行服务:
bun run server.ts访问http://localhost:3000,接口会返回一条 JSON 消息。
这个示例说明,Bun 作为一个完整运行时,自带 Web 标准 API。你不需要安装 express 或 koa,就能写一个常见的 JSON API。
6.2 用内置打包器构建前端代码
Bun 内置的打包器使用起来非常简单。假设目录下有一个前端入口文件:
// 文件路径:client.ts import chalk from "chalk"; const app = document.getElementById("app"); if (app) { app.textContent = chalk.green("Frontend bundled by Bun"); }执行构建命令:
bun build ./client.ts --outdir ./dist --minifyBun 会自动解析 TypeScript、处理依赖引入,并输出压缩后的 JS 文件。整个构建过程不需要额外配置 Webpack 或 esbuild。
6.3 用内置测试运行器写单元测试
创建一个测试文件:
// 文件路径:math.test.ts import { describe, expect, test } from "bun:test"; function add(a: number, b: number) { return a + b; } describe("add function", () => { test("adds two numbers", () => { expect(add(1, 2)).toBe(3); }); test("handles negative numbers", () => { expect(add(-1, -2)).toBe(-3); }); });执行测试:
bun test输出会以列表形式展示每个测试用例的运行结果。Bun 的测试运行器天然支持 TypeScript,不需要额外的 Babel 或 ts-jest,这是它相比 Jest 的一个重要优势。
7. Bun 1.4 与 npm、pnpm、Yarn 的对比与迁移建议
7.1 横向对比
| 对比项 | Bun 1.4 | npm | pnpm | Yarn |
|---|---|---|---|---|
| 依赖安装速度 | 快 | 中等 | 较快 | 中等 |
| 磁盘空间利用 | 一般,但自动清理有效 | 差,重复安装多 | 好,全局共享内容寻址存储 | 一般 |
| 源码级依赖分析 | 支持 | 不支持 | 不支持 | 不支持 |
| 运行时功能 | 完整运行时 | 无 | 无 | 无 |
| 测试运行器 | 内置 | 需单独安装 | 需单独安装 | 需单独安装 |
| 对 Node 兼容性 | 大多数场景兼容 | 原生兼容 | 原生兼容 | 原生兼容 |
npm、pnpm、Yarn 的定位都是“包管理器”,做的是安装依赖这件事。Bun 的定位则是“全套工具链”,包管理只是其中一个模块,而且它额外提供了源码依赖分析能力。这是本质区别。
7.2 迁移时容易踩坑的地方
从 Node.js 迁移到 Bun,最容易出问题的是原生模块。
如果项目依赖了 node-gyp 编译出来的原生模块,使用这些需要特定编译环境的包时,Bun 不一定能直接兼容。比较稳妥的做法是,先在一个小项目里跑通 Bun 的安装、测试、构建流程,再逐步扩展到核心项目。
7.3 什么项目不建议立刻迁移
下面情况建议暂缓:
- 项目重度依赖冷门的 Node.js 原生模块;
- 依赖了较多 CSP(Content Security Policy)相关或 Electron 供应链特殊要求的工具;
- 团队没有意愿改变现有工作流。
Bun 的定位不是让所有人明天就把 Node.js 删掉,而是提供一个新的选择。先把依赖清理能力用起来,已经能让项目受益很大。
8. 常见问题与排查思路
依赖管理本身就是一个充满不确定性的领域。下面整理几个遇到概率较高的问题。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| bun install 后提示找不到某个模块 | 依赖被自动清理误判 | 查看源码中是否动态引用该依赖 | 在代码中改为静态 import,或恢复依赖声明并在文档中说明用途 |
| 自动清理后运行时某个功能报错 | 部分依赖只在运行时被 require 或由 postinstall 脚本引用 | 检查报错栈和上线前 git diff | 保留必要依赖,并确认该依赖确实在当前页面或功能中被直接使用 |
| 从 npm 迁移后 lockfile 变化巨大 | Bun 使用 bun.lock 记录依赖结构 | 查看 git diff 对比内容 | 提交 bun.lock,让团队统一使用 Bun 安装依赖 |
| Windows 下某些原生模块无法运行 | 原生模块与 Bun 的兼容性不足 | 查看错误堆栈和构建日志 | 在 WSL 或 Docker 中运行,或继续使用 Node.js |
| 依赖清理后,package.json 变更过多 | 项目历史包袱重,很多废弃依赖积压 | 仔细 review 变更内容 | 分批清理,一次处理少量项目,不要边改边发版 |
重点提示:依赖自动清理对常规项目很安全,但不要跳过代码评审。删除依赖前,至少在测试环境里跑一遍核心流程。
9. 工程实践与团队落地建议
9.1 把依赖清理接入 CI
依赖清理最好进入自动化流程。可以在 CI 里增加一个检查任务:
bun install --frozen-lockfile bun pm ls--frozen-lockfile会锁定依赖版本,避免因为 lockfile 不一致导致 CI 和本地安装结果不同。
在 CI 中跑一遍 Bun 的依赖分析,及时发现新的无效依赖被引入。
9.2 自动清理不能代替人工 review
自动清理解决的是“哪些依赖完全没被引用”的问题,但它无法判断“某些依赖虽然被引用了,但业务上已经不再需要”。
比如一个项目从 Vue 2 迁移到 Vue 3,可能还存在少量兼容层代码引用了旧工具库。Bun 会发现它被 import,所以不会清理。这类历史包袱需要人工处理。
9.3 小项目先行,再逐步扩展
给团队的建议是:第一周选一个非核心、无用户量的项目,整个依赖安装和清理流程跑通;第二周扩大到维护频率较低的后台系统;稳定运行后再考虑核心业务项目。
9.4 容器镜像构建里的优势
Docker 容器里安装依赖,最怕的就是依赖体积大、安装速度慢。Bun 的依赖安装速度快,且能在构建后自动清理无效依赖,缩小镜像尺寸。
一个常见的 Dockerfile 示例:
FROM oven/bun:1 WORKDIR /app COPY package.json bun.lock ./ RUN bun install --frozen-lockfile COPY . . RUN bun build ./index.ts --outdir ./dist CMD ["bun", "run", "./dist/index.js"]利用 Bun 的官方镜像,配合依赖锁定和源码级清理,可以做出更快、更小的容器镜像。
10. 总结与下一步实践建议
Bun 1.4 最值得关注的不是某个性能数字,而是它把依赖治理从“人工定期清理”变成了“安装时默认执行”的能力。依赖管理工具的职责边界被真正拓宽了:不只是忠实地安装你声明的包,而是会反过来告诉你,哪些声明已经失去了意义。
如果你想亲手验证,建议按下面的路径来:
第一步,安装 Bun,用bun --version确认版本。第二步,新建一个测试项目,故意安装两三个依赖但不在代码里引用。第三步,运行bun install,观察 Bun 是否能识别出这些无效依赖。第四步,把 Bun 的内置测试运行器和打包器用起来,感受一套工具链完成所有事情的体验。第五步,确认稳定后,再选一个真实项目试水。
最后提醒一点:不管工具怎么自动清理,依赖治理始终需要人来把关。Bun 1.4 帮你扫掉了 80% 的机械性工作,剩下的 20% —— 比如某个依赖到底还有没有业务价值,仍然需要开发者根据业务上下文来判断。
希望这篇内容能帮你少踩一些依赖管理方面的坑。建议收藏备用,也欢迎在实际验证之后回来交流 Bun 1.4 在你的项目里清掉了多少个无效依赖。