Chapter 02

bun install:包管理

比 npm 快 25 倍的包管理器——完全兼容 package.json 与 npm registry,却用上了二进制链接与并行下载。

2.1 为什么 bun install 这么快

  1. 并行下载——用 Zig 的 async IO 同时发起几十个 HTTPS 连接。
  2. 全局缓存 + binlink——包只下载一次,全系统共享;项目的 node_modules 里用 hardlink/symlink 引用,不复制文件。
  3. 优化过的 JSON 解析器——npm 花大量时间解析大 package-lock.json;Bun 用 Zig 原生 JSON 解析快得多。
  4. 跳过无用工作——检测到锁文件没变就不重新求解依赖图。
效果对比(中等项目 500 依赖)
npm install   30s
pnpm install   8s
yarn install  12s
bun install    1.2s

2.2 基础命令

$ bun install                       # 安装 package.json 全部依赖
$ bun i                             # 别名

$ bun add react                     # 加依赖
$ bun add react@19                  # 指定版本
$ bun add -d vitest                 # devDependencies
$ bun add -g typescript             # 全局安装
$ bun add --peer react              # peerDependencies
$ bun add --optional fsevents       # optionalDependencies

$ bun remove react                  # 移除
$ bun update                        # 按 semver 范围升级
$ bun update react                  # 只升一个
$ bun outdated                      # 查看有更新的包

2.3 锁文件:bun.lock / bun.lockb

Bun 有两种锁文件:

bun.lock(v1.1.33+ 默认)
JSON 文本格式,人类可读、diff 友好、审计方便。推荐
bun.lockb(早期默认)
二进制格式,压缩后体积小、解析更快,但 git diff 不可读。旧项目里常见。

新项目会默认生成 bun.lock。从 lockb 迁过来:

$ bun install --save-text-lockfile   # 产出 bun.lock 并删除 .lockb

2.4 CI 推荐:--frozen-lockfile

$ bun install --frozen-lockfile

等价 npm 的 npm ci

2.5 Workspace(Monorepo)

Bun 原生支持 pnpm 风格的 workspace。根目录 package.json:

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["packages/*", "apps/*"]
}
my-monorepo/
├── package.json           # 根(上面那份)
├── bun.lock               # 整个 monorepo 一份锁文件
├── packages/
│   ├── ui/
│   │   └── package.json   # "name": "@myorg/ui"
│   └── utils/
│       └── package.json   # "name": "@myorg/utils"
└── apps/
    └── web/
        └── package.json   # 依赖 "@myorg/ui": "workspace:*"
# apps/web/package.json
{
  "dependencies": {
    "@myorg/ui": "workspace:*"
  }
}

workspace:* 协议告诉 Bun"用本 monorepo 内的版本"——符号链接而非从 npm 下载。

跨 workspace 命令

$ bun install                    # 根目录跑,装所有 workspace
$ bun --filter '@myorg/*' run build   # 跑匹配 workspace 的 build
$ bun --filter web run dev       # 只跑 web 的 dev

2.6 trustedDependencies:postinstall 脚本白名单

默认情况下,Bun 不会执行依赖包里的 postinstall 脚本——这是供应链安全考量(恶意包可能利用 postinstall 搞事)。

需要某些包的 postinstall(如 esbuildsharp、Electron 原生模块)时,在 package.json 里显式授权:

{
  "trustedDependencies": [
    "esbuild",
    "sharp",
    "electron"
  ]
}
为何这是安全的默认

2021 年 ua-parser-js、2022 年 colors/faker 事件都是通过 postinstall 执行恶意代码。Bun 逆转默认:除非你明确信任,否则所有 postinstall 都被跳过。

2.7 私服与 scope

# bunfig.toml 或 ~/.bunfig.toml
[install]
registry = "https://registry.npmmirror.com"    # 国内镜像

[install.scopes]
"@my-org" = { url = "https://npm.company.com", token = "$NPM_TOKEN" }
"@internal" = "https://npm.company.com"

[install.cache]
dir = "~/.bun/install/cache"
disable = false

[install.lockfile]
save = true
print = "yarn"    # 同时输出一份 yarn.lock 便于兼容

2.8 Peer Dependency 处理

Bun 默认:

不想自动安装 peerDeps:

[install]
peer = false

2.9 bun pm:补充工具

$ bun pm ls                         # 列出已安装包的树
$ bun pm ls --all                   # 含间接依赖
$ bun pm cache                      # 缓存目录
$ bun pm cache rm                   # 清缓存
$ bun pm trust <pkg>                # 添加到 trustedDependencies
$ bun pm trust --default            # 信任所有(危险)
$ bun pm untrusted                  # 列出被阻止的 postinstall 脚本
$ bun pm why lodash                 # 解释为什么装了这个包

2.10 与 npm/pnpm 的兼容性

bun
package.json100% 兼容
npm registry
.npmrc部分(建议迁到 bunfig.toml)
pnpm-workspace.yaml不支持(用 package.json 的 workspaces 字段)
pnpm overrides支持 overrides 字段
yarn.lock / package-lock.json首次 bun install 会读一次然后生成 bun.lock

2.11 小结