隔离安装
与 pnpm 方法类似的严格依赖隔离
Bun 提供了一种名为 隔离安装 的替代软件包安装策略,可创建类似于 pnpm 方法的严格依赖隔离。此模式可防止幽灵依赖(导入自身未声明依赖的软件包),并使构建过程可复现且具有确定性。
对于新建的工作区/monorepo 项目(锁文件中包含 configVersion = 1),隔离安装是默认方式。除非进行显式配置,否则现有项目会继续使用提升式安装。
什么是隔离安装?
隔离安装创建了非提升的依赖结构,包只能访问它们显式声明的依赖。这有别于 npm 和 Yarn 传统的“提升”安装策略,即依赖项被扁平化到共享的 node_modules 目录中。
主要优点
- 防止幻影依赖 — 包不能意外导入未声明的依赖
- 确定性解析 — 无论安装了什么,依赖树相同
- 更适合多包仓库 — 工作区隔离防止包之间的交叉污染
- 可复现构建 — 在不同环境下解析行为更可预测。
使用隔离安装
命令行
使用 --linker 标志指定安装策略:
# 使用隔离安装
bun install --linker isolated
# 使用传统提升安装
bun install --linker hoisted
配置文件
在你的 bunfig.toml 或全局 $HOME/.bunfig.toml 中设置默认 linker 策略:
[install]
linker = "isolated"
默认行为
默认 linker 策略取决于项目锁文件的 configVersion:
configVersion | 是否使用工作区? | 默认 Linker |
|---|---|---|
1 | ✅ | isolated |
1 | ❌ | hoisted |
0 | ✅ | hoisted |
0 | ❌ | hoisted |
新项目:默认为 configVersion = 1。工作区中,v1 默认使用隔离 linker;否则使用提升链接。
已有 Bun 项目(v1.3.2 之前创建):如果现有锁文件未标明版本,运行 bun install 时 Bun 会将 configVersion 设为 0,保持之前的提升 linker 默认。
从其他包管理器迁移:
- 来自 pnpm:
configVersion = 1(工作区中使用隔离安装) - 来自 npm 或 yarn:
configVersion = 0(使用提升安装)
通过传递 --linker 标志或在配置文件中设置来覆盖默认值。
隔离安装如何工作
目录结构
隔离安装不提升依赖,而是创建两层结构:
node_modules/
├── .bun/ # Central package store
│ ├── node_modules/ # Hoisted fallback (see install.hoist)
│ │ └── package -> ../package@1.0.0/node_modules/package
│ ├── package@1.0.0/ # Versioned package installations
│ │ └── node_modules/
│ │ └── package/ # 实际包文件
│ ├── @scope+package@2.1.0/ # 作用域包 (+ 代替 /)
│ │ └── node_modules/
│ │ └── @scope/
│ │ └── package/
│ └── ...
└── package-name -> .bun/package@1.0.0/node_modules/package # 符号链接
存储目录名称中 @ 之后的部分是包的解析标识。对于注册表包,该部分是其版本号。对于文件夹、tarball、git 或 GitHub 依赖,Bun 会根据路径或 URL(以及提交)生成该部分,因此长度可能很长。Bun 最多写入其中的 80 个字节:如果解析标识较长,就将其截断到最多 63 个字节,然后附加 + 和根据完整解析标识生成的 16 个十六进制数字。因此,存储中包的路径最多比项目目录长 34 + 2 * <name length> + 80 个字符(如果包有 peer 依赖,则再多 17 个字符)。这在 Windows 上很重要:超过 260 个字符的路径仍可供 Bun 本身使用,但不能作为包生命周期脚本的工作目录。
解析算法
- 中央存储 — Bun 将所有包安装到
node_modules/.bun/package@version/目录中 - 符号链接 — 顶层
node_modules包含指向中央存储的符号链接 - Peer 解析 — 复杂的 peer 依赖会创建专用目录名称
- 去重 — 具有相同包 ID 和 peer 依赖集的包会被共享
- 重新链接 — 后续安装时,如果某个依赖被重新解析,Bun 会复用现有存储条目并重新指向其符号链接。不再被引用的存储条目会保留,直到运行
bun prune
工作区处理
在 monorepo 中,Bun 会以特殊方式处理工作区依赖:
- 工作区包 — 直接链接到其源目录,而不是存储目录
- 工作区依赖 — 可以访问 monorepo 中的其他工作区包
- 外部依赖 — 安装在隔离存储中
与提升安装的比较
| 方面 | 提升(npm/Yarn) | 隔离(类似 pnpm) |
|---|---|---|
| 依赖访问 | 包可访问任何提升的依赖 | 包只能看到声明的依赖 |
| 幻影依赖 | ❌ 可能发生 | ✅ 防止 |
| 磁盘使用 | ✅ 较低(共享安装) | ✅ 类似(使用符号链接) |
| 确定性 | ❌ 较低 | ✅ 更高 |
| Node.js 兼容性 | ✅ 标准行为 | ✅ 通过符号链接兼容 |
| 适用场景 | 单项目,旧代码 | 多包仓库,严格依赖管理 |
高级功能
对等依赖处理
隔离安装会将对等依赖编码到存储路径中:
# 有 peer 依赖的包创建特化路径
node_modules/.bun/package@1.0.0_react@18.2.0/
目录名称同时包含包版本及其对等依赖版本,因此每种唯一组合都会获得自己的安装。
使用 install.hoist = false 进行严格解析
默认情况下,Bun 会创建 node_modules/.bun/node_modules,这是一个回退目录,其中包含指向每个已安装包的符号链接。由于此目录是每个存储条目的祖先目录,存储中的包可以解析它从未声明的依赖。设置 install.hoist = false(或在 .npmrc 中设置 hoist=false,与 pnpm 一致)即可跳过此目录的创建。此后,未声明的导入会失败,而不会依赖恰好安装的其他包。这里有一个与 pnpm 相同的注意事项:根 node_modules 也位于存储目录之上。因此,链接到该目录中的包(你的直接依赖、匹配 publicHoistPattern 的包,以及工作区包)仍可从任何存储包中解析:
[install]
linker = "isolated"
hoist = false
install.hoistPattern 和 install.publicHoistPattern 分别提供了基于模式控制同一回退目录和根 node_modules 的方式。
全局虚拟存储
启用 install.globalStore 后,Bun 会将存储条目一次性物化到 <cache>/links/ 下的全局虚拟存储中,而 node_modules/.bun/<pkg>@<ver> 则是指向该存储的符号链接。在典型的中型项目中,运行 rm -rf node_modules 后进行热安装,每个包只需创建一个符号链接,而无需再次复制每个包的所有文件,速度大约快 7 倍。全局存储默认关闭;有关如何启用、完整布局、基准测试和权衡,请参阅全局存储文档。
后端策略
当全局存储被禁用(默认)或某个条目不适合使用时,Bun 会使用以下方式之一将条目物化到项目中:
- Clonefile(macOS)— 写时复制文件系统克隆
- Hardlink(Linux/Windows)— 用于节省磁盘空间的硬链接
- Copyfile(回退方案)— 在其他方法不可用时复制完整文件
调试隔离安装
启用详细日志记录以查看安装过程:
bun install --linker isolated --verbose
详细输出会显示:
- 存储条目创建
- 符号链接操作
- 对等依赖解析
- 去重决策
故障排除
兼容性问题
一些包可能不兼容隔离安装,原因包括:
- 硬编码路径 — 假设
node_modules结构扁平 - 动态导入 — 运行时导入不遵循 Node.js 解析
- 构建工具 — 直接扫描
node_modules
遇到问题时可:
-
对特定项目切换回提升模式:
terminalbun install --linker hoisted -
报告兼容性问题,帮助改进隔离安装支持
性能考虑
- 安装时间 — 可能略慢,因符号链接操作
- 磁盘使用 — 与提升类似(使用符号链接,不复制文件)
- 内存使用 — 因复杂 peer 解析,安装时更高。
迁移指南
从 npm/Yarn 迁移
# 删除现有 node_modules 和锁文件
rm -rf node_modules package-lock.json yarn.lock
# 使用隔离 linker 安装
bun install --linker isolated
从 pnpm 迁移
隔离安装在概念上与 pnpm 类似,因此迁移是直接的:
# 删除 pnpm 文件
rm -rf node_modules pnpm-lock.yaml
# 使用 Bun 的隔离 linker 安装
bun install --linker isolated
这些布局很相似:Bun 会将包从其全局缓存硬链接(在 macOS 上则克隆)到每个项目的存储目录 node_modules/.bun/,并将顶层 node_modules 条目符号链接到该目录。启用 install.globalStore 后,这些存储条目会改为符号链接到全局虚拟存储。
何时使用隔离安装
适合使用隔离安装的情况:
- 在多包仓库中工作
- 需要严格依赖管理
- 需要防止幻影依赖
- 构建需依赖确定性的库
适合使用提升安装的情况:
- 处理假设
node_modules扁平的旧代码 - 需要兼容现有构建工具
- 在符号链接支持不佳的环境中工作
- 偏好传统的 npm 行为
相关文档
- 包管理器 > 工作区 — 多包仓库工作区管理
- 包管理器 > 锁文件 — 理解 Bun 的锁文件格式
- CLI > install — 完整的
bun install命令参考。