bun install 入门
本教程共 34 篇 · 第 11 篇 · 更新于 2026-08-06
本节目标:
- 理解
bun install的定位:它是 Bun 内置、与 Node.js 项目兼容的包管理器,可直接替换npm/yarn/pnpm。- 掌握
bun install、bun add、bun remove、bunx的常用命令与等价写法。- 了解 Bun 在依赖安装上的两个关键取向——速度与默认安全(不执行依赖的生命周期脚本)。
- 会用
--production、--frozen-lockfile等标志做可复现安装,并理解非 npm 来源(Git、tarball、全局)依赖。
11.1 Bun 内置的包管理器
Bun 不只是一个运行时,它还内置了一套 npm 兼容的包管理器。只要你的项目里有 package.json,无论原本用的是 npm、yarn 还是 pnpm,都可以直接在项目根目录运行:
bun install
这条命令会读取 package.json 中的 dependencies、devDependencies、optionalDependencies 与 peerDependencies,把它们安装到 node_modules,并写入一份 bun.lock 锁文件。也就是说,你不需要为了用 Bun 而迁移整个工程——很多团队是先拿 bun install 替换 npm install,体验更快的安装速度,再逐步把 bun run、bun test 也用起来。
Note官方文档给出的对比数据是:把任意 Node.js 项目里的
npm install换成bun install,安装速度最高可快约 25 倍。这个差异主要来自 Bun 在依赖解析、缓存与文件拷贝(hardlink / clonefile)层面的工程优化,而非某一个单独的”黑科技”。
11.2 安装全部依赖
最基础的用法就是不带任何参数:
bun install
执行时,Bun 会做三件事:
- 安装
dependencies、devDependencies、optionalDependencies;peerDependencies默认也会一并安装(行为更接近 Yarn)。 - 运行 你自己项目的
{pre|post}install与{pre|post}prepare脚本。 - 写入
bun.lock锁文件到项目根目录。
需要留意的是第二点里的”安全边界”:Bun 不会执行已安装依赖包里的 {pre|post}install 等生命周期脚本。这是 Bun 有意设计的默认安全策略(详见第 14 章”生命周期脚本”),目的是降低供应链攻击面。如果你确实需要一个依赖运行它的 postinstall(例如 sharp、esbuild 这类需要下载平台二进制文件的包),需要把它加入 trustedDependencies(第 14 章会展开)。
第一次跑完之后再执行一次 bun install,你会发现它几乎瞬间返回。原因在于 Bun 会先比对 package.json 与 bun.lock 是否仍然吻合:吻合就直接进入”校验并补齐缺失文件”的快路径,不再向 registry 重新请求版本信息;只有当你改了依赖声明、或锁文件根本不存在时,才会触发完整的解析流程。理解这一点很有用——如果你发现每次安装都很慢,通常不是网络问题,而是锁文件没有被正确复用,比如它被误加进了 .gitignore,或者 CI 每次都在一个干净目录里从头解析。
调高或调低日志输出也很简单:
bun install --verbose # 输出调试日志
bun install --silent # 不输出日志
--verbose 在排查”某个包为什么解析到了这个版本”时特别有价值:它会打印出每一步的请求与命中缓存的情况,比盯着最终的 node_modules 反推要直接得多。
11.3 添加与移除依赖:bun add / bun remove
添加依赖用 bun add,它等价于 npm install <pkg>:
bun add preact # 添加到 dependencies
bun add zod@3.20.0 # 指定精确版本
bun add zod@^3.0.0 # 指定版本范围
bun add zod@latest # 指定 tag(latest)
把它加为开发依赖、可选依赖或 peer 依赖,对应不同的标志:
bun add --dev @types/react # 等价 --development / -d / -D,写入 devDependencies
bun add --optional lodash # 写入 optionalDependencies
bun add --peer @types/bun # 写入 peerDependencies
bun add react --exact # 等价 -E,写入精确版本而非范围
--exact(或 -E)的区别很直观:不加时 package.json 里写的是 "react": "^18.2.0"(匹配 >=18.2.0 <19.0.0);加上后写的是 "react": "18.2.0",只锁定这一个版本。
移除依赖则是 bun remove,等价于 npm uninstall:
bun remove ts-node
Tip
bun add和bun remove都会同步更新package.json与bun.lock。需要查看当前支持的全部选项时,运行bun add --help即可。
11.4 运行一次性命令:bunx
bunx 是 bun x 的别名,相当于 npx 或 yarn dlx:它会优先查找本地已安装的包,找不到时再从 npm 自动下载并运行。安装 Bun 时 bunx 会一并可用。
bunx cowsay "Hello world!"
很多 CLI 工具在 package.json 的 "bin" 字段里声明可执行文件。你可以用 bunx 直接调用它们;如果可执行文件名和包名不一致,用 -p / --package 指定包:
bunx --package @angular/cli ng # 指定从 @angular/cli 运行 ng
bunx -p renovate renovate-config-validator
默认情况下,bunx 会尊重可执行文件里的 shebang。如果某个文件写了 #!/usr/bin/env node,Bun 会启动一个 node 进程来跑它;想改用 Bun 运行时执行,加 --bun 标志(注意它要放在可执行文件名之前):
bunx --bun my-cli # 用 Bun 运行
bunx my-cli --bun # 错误:--bun 会被当作 my-cli 的参数
Note官方文档称,由于 Bun 启动极快,
bunx运行本地已安装包时大约比npx快 100 倍。跨平台下载的包会进入 Bun 的全局缓存(见第 14 章),再次运行无需重复下载。
11.5 生产模式与可复现安装
在 CI / 生产环境,你通常希望只装 dependencies 并得到完全确定的结果:
bun install --production # 不安装 devDependencies
bun install --frozen-lockfile # 严格按照 bun.lock 安装,不更新锁文件
--frozen-lockfile 的含义是:完全按锁文件里的版本安装;如果 package.json 与 bun.lock 不一致,Bun 会直接报错退出,而不是悄悄修改锁文件。这正是”可复现构建”想要的严格行为。
Warning使用
--frozen-lockfile(或等价的bun ci,见第 15 章)时,必须先把bun.lock提交到版本库。否则锁文件缺失,Bun 无法做冻结安装。
还有一个容易被忽略的细节:Bun 会检测 CI 环境。当环境变量 CI=true 时,bun install 会自动启用冻结行为,效果等同于显式加了 --frozen-lockfile。也就是说在 GitHub Actions、GitLab CI 这类默认设置了 CI 变量的平台上,即便你什么标志都没写,package.json 与锁文件不一致同样会让构建失败。这个默认值是好事——它把”悄悄改锁文件”这条路堵死了。反过来,如果某个流水线确实需要更新锁文件(比如一个专门用来升级依赖、然后自动开 PR 的定时任务),就要显式加 --no-frozen-lockfile 把它关掉。
你也可以用 --omit 精细排除某一类依赖:
bun install --omit dev # 排除 devDependencies
bun install --omit=dev --omit=peer --omit=optional
想先”演习”一遍、看看会发生什么而不真正安装,加 --dry-run 即可。
11.6 全局安装
和 npm 一样,Bun 支持全局安装命令行工具(不会修改当前项目的 package.json):
bun install --global cowsay # 等价 bun install -g / bun add -g cowsay
cowsay "Bun!"
全局包与它们的可执行文件分别落在 ~/.bun/install/global 与 ~/.bun/bin,可在 bunfig.toml 里通过 install.globalDir / install.globalBinDir 配置。
11.7 非 npm 来源的依赖
除了从 npm registry 安装,Bun 也支持 Git、GitHub 以及本地/远程 tarball 作为依赖来源。直接写进 package.json 即可:
{
"dependencies": {
"dayjs": "git+https://github.com/iamkun/dayjs.git",
"lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21",
"zod": "github:colinhacks/zod",
"react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
}
}
也可以用 bun add 一步写好:
bun add git@github.com:moment/moment.git
bun add zod@https://registry.npmjs.org/zod/-/zod-3.21.4.tgz
11.8 安装策略:hoisted 与 isolated
Bun 支持两种 node_modules 组织方式,通过 --linker 控制:
bun install --linker hoisted # 传统平铺,类似 npm/Yarn
bun install --linker isolated # 严格隔离,类似 pnpm,杜绝"幽灵依赖"
默认策略取决于场景:新建的单包项目用 hoisted(贴近 npm 习惯),新建的工作区/monorepo用 isolated(防止未声明却能引用的”幽灵依赖”),旧项目则保留 hoisted 以保证向后兼容。相关细节会在第 13、14 章深入。
Tip想要更可控的安装行为(是否装 dev/optional、并发脚本数、默认 linker 等),把这些配置写进
bunfig.toml的[install]段即可,免得每次都在命令行加一长串标志。
11.9 常见问题速答
Q:bun install 和 bun i 有区别吗?
没有区别。bun i 是 bun install 的官方简写,两者行为完全一致。类似地,bun add --dev 也可以写成 -d、-D 或 --development,挑顺手的用即可,不必纠结。
Q:装完之后,为什么代码里 import 一个没在 package.json 里声明过的包会失败?
如果当前用的是 isolated 策略(新建工作区项目的默认值),只有显式声明过的依赖才对你可见——这正是它的设计意图,用来杜绝”幽灵依赖”。这类报错其实是在帮你提前发现问题:把缺失的包用 bun add 正式加进来,远比切回 hoisted 掩盖问题要稳妥。
Q:能不能只用 Bun 装依赖,仍然用 Node.js 跑代码?
完全可以,而且这是引入 Bun 风险最低的方式。bun install 生成的是标准的 node_modules 目录结构,Node.js 可以直接读取,不需要任何额外适配。很多团队的第一步就是只把 Bun 当”更快的 npm”,运行时继续用 Node.js,跑顺了再考虑后面的事。
Q:bunx 和 bun run 该怎么选?
bun run <script> 执行的是 package.json 里 scripts 字段定义的脚本;bunx <pkg> 执行的是某个包提供的可执行文件,本地没装时会临时下载。日常的构建、测试、启动用前者;临时用一次某个脚手架或校验工具(比如生成一个项目模板)用后者。
Q:安装报错说某个原生包缺二进制文件,怎么办?
这几乎总是因为它的 postinstall 被默认拦截了。先用 bun pm untrusted 看看哪些包的脚本被拦下来,确认来源可信后再用 bun pm trust <name> 放行,或者直接写进 package.json 的 trustedDependencies。第 14 章会完整讲这套机制。
11.10 小结
bun install是 Bun 内置、npm 兼容的包管理器,可直接用于既有 Node.js 项目,安装速度明显更快。- 日常增删依赖用
bun add/bun remove;运行一次性 CLI 用bunx。 - Bun 默认不执行依赖包的生命周期脚本,需要时用
trustedDependencies放行——这是一项安全取舍,而非缺陷。 - 生产/CI 用
--production与--frozen-lockfile(bun ci)保证可复现;Git、tarball、全局安装等非标准来源也都受支持。
下一章我们会专门聊 bun.lock 这份锁文件:它为什么是文本格式、相比旧的二进制 bun.lockb 好在哪里、以及迁移方法。