包管理器对比与技巧
本教程共 34 篇 · 第 15 篇 · 更新于 2026-08-06
本节目标:
- 客观了解 Bun 包管理器与 npm / yarn / pnpm 的关系与差异,重点是命令对应与兼容点。
- 掌握
bun ci在 CI 中做可复现安装的方法。- 拿到一份常用命令速查表与
bunfig.toml持久化配置示例。- 了解从其它包管理器迁移、以及
bun pm系列实用工具。
15.1 先说兼容性
Bun 的包管理器是为兼容 npm 生态而生的:它读取标准 package.json、解析 npm registry、支持 dependencies / devDependencies / optionalDependencies / peerDependencies,并自动迁移 npm、yarn、pnpm 的锁文件(见第 12 章)。这意味着在大多数既有 Node.js 项目里,你可以用 bun install 直接替换 npm install,无需改动业务代码。
需要强调的是:Bun 并非要”取代”或”否定”其它工具,而是提供了一种更快、且默认更安全的安装体验。下面以客观视角对比几个常被关心的点。
| 关注点 | npm | yarn (v1) | pnpm | Bun |
|---|---|---|---|---|
| 锁文件 | package-lock.json | yarn.lock | pnpm-lock.yaml | bun.lock(文本 JSONC,v1.2 起默认) |
| 依赖隔离 | 平铺 | 平铺 | 严格隔离 | 可选(isolated / hoisted) |
| 生命周期脚本 | 默认执行 | 默认执行 | 默认执行 | 默认不执行(需 trustedDependencies) |
| 一键运行 CLI | npx | yarn dlx | pnpm dlx | bunx |
| 安装速度 | 基准 | 较快 | 快 | 快(官方称最高约 25x 于 npm) |
Note“安装速度”受项目规模、网络、缓存状态影响很大,上面的倍数来自 Bun 官方文档的基准,仅供量级参考。不同工具各有适用场景,按团队习惯选择即可。
15.2 CI 中的可复现安装:bun ci
在 CI / CD 里,你通常希望严格按锁文件安装、一旦 package.json 与锁文件不一致就直接失败,而不是悄悄更新锁文件。Bun 提供了 bun ci:
bun ci
它等价于 bun install --frozen-lockfile。前提是你已经把 bun.lock 提交到了版本库。在 GitHub Actions 中的典型用法:
# .github/workflows/ci.yml
name: ci
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2 # 官方 action 安装 bun
- run: bun ci # 严格按锁文件安装
- run: bun run build
Warning若 CI 里用
bun ci却没提交bun.lock,安装会因锁文件缺失而失败。请确保锁文件随代码一起入库。
15.3 常见命令速查
把日常最常用的一批命令对照列出,方便从其它工具迁移:
# 安装
bun install # 安装全部依赖(≈ npm install)
bun install <pkg> # 添加依赖(≈ npm install <pkg>)
bun add <pkg> # 同上,语义更明确
bun add -d <pkg> # 开发依赖(≈ npm install -D)
bun add -g <pkg> # 全局安装(≈ npm install -g)
bun remove <pkg> # 移除依赖(≈ npm uninstall)
bunx <pkg> # 运行一次性 CLI(≈ npx)
# 安装变体
bun install --production # 仅 dependencies(≈ npm ci --production 思路)
bun install --frozen-lockfile # 严格按锁文件(≈ npm ci)
bun install --lockfile-only # 只更新锁文件,不装 node_modules
bun install --dry-run # 演习,不真正安装
# 可复现 / 校验
bun ci # 等价 bun install --frozen-lockfile
15.4 用 bunfig.toml 持久化配置
不想每次都在命令行加一堆标志?把常用安装行为写进 bunfig.toml 的 [install] 段,项目级与用户级(~/.bunfig.toml)会被合并:
[install]
optional = true # 是否安装 optionalDependencies
dev = true # 是否安装 devDependencies
peer = true # 是否安装 peerDependencies
production = false # 等价 --production
saveTextLockfile = true # 等价 --save-text-lockfile(v1.2+ 默认即文本锁)
frozenLockfile = false # 等价 --frozen-lockfile
dryRun = false # 等价 --dry-run
concurrentScripts = 16 # 生命周期脚本最大并发数
linker = "hoisted" # 或 "isolated"
ignoreScripts = false # 等价 --ignore-scripts(全局禁用生命周期脚本)
minimumReleaseAge = 259200 # 最小发布年龄(秒)
Tip环境变量优先级高于
bunfig.toml。几个常用环境变量:BUN_CONFIG_REGISTRY(指定 registry,默认https://registry.npmjs.org)、BUN_CONFIG_TOKEN(鉴权 token)、BUN_CONFIG_YARN_LOCKFILE(额外写 yarn.lock)、BUN_CONFIG_SKIP_SAVE_LOCKFILE(不写锁文件)。
15.5 从其它包管理器迁移
- npm:直接把
package-lock.json留在原地,运行bun install,它会自动迁移并生成bun.lock;原文件保留,核对后可删。 - yarn:
yarn.lock(v1)会被自动迁移;如需额外保留一份 yarn 风格锁文件,用bun install --yarn。 - pnpm:检测到
pnpm-lock.yaml且无bun.lock时自动迁移,还会把pnpm-workspace.yaml里的 workspaces、catalogs、overrides、patchedDependencies搬到package.json;要求 pnpm 锁文件版本 ≥ 7。迁移后pnpm-lock.yaml/pnpm-workspace.yaml可手动删除。
Note迁移只在
bun.lock不存在时触发,且没有”退出”开关。如果你只是想试试 Bun 而不想改原锁文件,建议先在新分支或临时目录验证。
15.6 bun pm 实用工具集
bun pm 是一组包管理相关的实用命令,日常排查很顺手:
bun pm ls # 列出已安装依赖及其解析版本(别名 bun list)
bun pm ls --all # 含所有深层依赖
bun pm ls --trusted # 只列被允许运行生命周期脚本的包
bun pm cache # 打印全局缓存路径
bun pm cache rm # 清空全局缓存
bun pm whoami # 打印当前 npm 用户名(需已登录)
bun pm hash # 打印当前锁文件哈希
bun pm trust <names> # 为未信任依赖放行并写入 trustedDependencies
bun pm untrusted # 列出被拦截了脚本的依赖
bun pm pack # 打包当前工作区为 .tgz(≈ npm pack)
bun pm bin # 打印本地 bin 目录;加 -g 打印全局 bin 目录
bun pm pkg get name # 读取 package.json 字段(支持点/括号记法)
bun pm pkg set version=2.0.0 # 修改 package.json 字段
bun pm trust 与 bun pm untrusted 特别适合排查”明明装了某原生包却没构建成功”的问题——先 untrusted 看哪些脚本被拦,再决定要不要 trust。
15.7 CI 与 Docker 中的实践
把 Bun 接进流水线时,有三件事值得一开始就做对,能省掉后面大量莫名其妙的排查时间。
第一,锁定 Bun 版本。 不同版本的 Bun 在写入或归一化锁文件时可能存在细微差异。本地用一个版本、CI 又下载了另一个版本,是”冻结安装莫名失败”最常见的成因。在 GitHub Actions 里显式指定版本,同时在 package.json 里声明 packageManager,让协作者和其它工具也能看到预期版本:
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
- run: bun --version
- run: bun ci
{
"packageManager": "bun@1.3.14"
}
升级 Bun 时,把版本号变更和锁文件更新放进同一个 pull request,这样评审的人能一眼看出两者的因果关系。
第二,Docker 里先拷贝清单、再安装依赖。 只把 package.json 与 bun.lock 拷进镜像,装完依赖之后再拷源码,这样日常的源码改动不会让昂贵的安装层缓存失效:
FROM oven/bun:1.3.14 AS base
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build
工作区项目要额外当心:只拷根 package.json 是不够的,Bun 还需要读取各子包的清单才能校验锁文件是否一致,必须把它们一并拷进去。同时检查 .dockerignore,过宽的忽略规则可能把 bun.lock 或嵌套的 package.json 挡在构建上下文之外——这种情况报错信息会指向锁文件,但真正的原因在忽略规则里。
第三,缓存全局安装缓存目录。 把 ~/.bun/install/cache 在多次运行之间持久化,并用 bun pm hash 的输出作为缓存 key(见第 12 章),才能真正吃到热安装的速度红利。只缓存 node_modules 而不缓存全局缓存,效果会打折扣。
15.8 常见误区与排查清单
切换工具时常遇到几个雷区,这里集中说明:
- “为什么装完某原生包却没生成二进制?” 多半是生命周期脚本被默认拦截了。先
bun pm untrusted看哪些脚本被拦,再对需要的包执行bun pm trust <name>或写进trustedDependencies。 - “CI 里
bun ci直接报错失败” 检查两点:bun.lock是否随代码入库、package.json是否与锁文件一致。不一致时要么更新锁文件提交,要么修正package.json。 - “迁移后原来的锁文件还在” 这是预期行为——Bun 会保留
package-lock.json/yarn.lock/pnpm-lock.yaml不动,核对无误后你可以手动删除,不必担心被覆盖。 - “改了本地依赖但引用方看不到” 多见于工作区场景,删掉
node_modules重新bun install通常能修复链接关系。 - “锁文件明明没动,CI 却报 lockfile had changes” 大概率是本地与 CI 的 Bun 版本不一致,或者有人手改了
package.json的版本号却没重跑安装。正确的修法是在本地执行bun install、检查git diff -- package.json bun.lock确认改动合理后提交新锁文件,而不是把--frozen-lockfile从 CI 里拿掉——那等于放任线上部署去挑选从未被评审、也从未被测试过的依赖版本。 - “我没加
--frozen-lockfile,CI 为什么还是冻结了?” 这是预期行为。Bun 检测到CI=true时会自动启用冻结安装(见第 11 章)。确实需要在流水线里更新锁文件时,显式加--no-frozen-lockfile。
Tip把 Bun 当”更快的 npm 替换”来用,是最平滑的起步方式:先只替换
bun install/bun add/bun remove,确认团队无碍后,再逐步引入bun run、bun test、bunx等能力,避免一次性大改带来的风险。
15.9 小结
- Bun 的包管理器与 npm 生态高度兼容,可直接用于既有项目;它与 npm/yarn/pnpm 在锁文件、隔离策略、生命周期脚本默认值上各有差异,应客观选择。
- CI 中用
bun ci(=bun install --frozen-lockfile)做严格可复现安装,记得提交bun.lock。 - 常用安装/增删/运行命令与 npm 直接对应;把习惯配置写进
bunfig.toml比每次敲 flag 更稳。 - 从 npm/yarn/pnpm 迁移基本是”跑一次
bun install”;bun pm系列工具便于日常排查与包管理操作。
至此,Bun 作为包管理器的一面已经讲完。从第 16 章起,我们会进入 Bun 作为**打包器(Bundler)**的能力,看看它如何把源码与资源打包成可部署的产物。