使用 npm-check-updates 实现更智能的包更新
依赖漂移会慢慢扼杀项目。你跳过几次更新,然后再跳过几次,突然之间你就面对着 47 个过时的包——其中一半有破坏性变更,一些存在安全漏洞,而且没有明确的前进路径。运行 npm update 感觉像是在玩俄罗斯轮盘赌。批量升级会破坏 CI。安全的选择变成了什么都不做,而这根本不安全。
npm-check-updates (ncu) 提供了一种不同的方法:将更新什么的决策与安装和测试的行为分离。这种区分比大多数团队意识到的更重要。
核心要点
- ncu 修改 package.json 版本范围而不触及 node_modules 或锁文件,让你控制何时进行安装
- 使用
--target minor或--target patch将更新限制为非破坏性变更 --peer标志可防止建议会导致对等依赖冲突的更新- 在测试更新后,始终将 package.json 和 package-lock.json 一起提交
- 使用 npm 的
overrides字段在处理漏洞时强制指定传递依赖的特定版本
为什么盲目升级会破坏构建
JavaScript 包管理的核心问题不在于发现过时的依赖——npm outdated 可以处理这个问题。问题在于升级到”最新版本”会忽略对等依赖约束、语义化版本边界,以及锁文件存在是有原因的这一现实。
当你运行 npm update 时,npm 会遵守 package.json 中的版本范围。即使版本 3 存在,它也不会从 ^2.0.0 跳到 3.0.0。这是有意为之的。但这也意味着需要范围变更的真正更新会无限期地卡住。
ncu 通过直接修改 package.json 来解决这个问题——更新版本范围——而不触及 node_modules 或锁文件。你可以控制何时进行安装。
Node.js 依赖管理的现代 ncu 工作流
全局安装不再是默认的思维模式。使用 ncu v18+,可以通过包管理器直接运行它:
npx npm-check-updates
# 或
pnpm dlx npm-check-updates
# 或
bunx npm-check-updates
这会检查更新而不修改任何内容。你会看到可用的更新,按语义化版本级别用颜色编码:主版本为红色,次版本为青色,补丁版本为绿色。
关键标志是 -u,它会将更改写入 package.json。但重要的是:这只更新清单文件。在你明确运行 npm install 之前,锁文件保持不变。
这种分离实现了更安全的依赖更新工作流:
- 运行
ncu -u --target minor用非破坏性变更更新 package.json - 运行
npm install重新生成锁文件 - 运行测试套件
- 如果测试通过,将 package.json 和 package-lock.json 一起提交
对于主版本更新,使用 ncu -u --filter package-name 单独处理,查看变更日志,然后测试。
Discover how at OpenReplay.com.
对等依赖和兼容性检查
对等依赖冲突导致大多数升级失败。React 组件库可能需要 React 18,但你的项目固定在 React 17。盲目升级该库会破坏构建。
ncu 的 --peer 标志在建议更新之前检查对等依赖兼容性。这可以防止提出会立即导致安装失败的升级建议。
为了更严格的控制,将其与 --target 结合使用:
ncu --peer --target minor
这只显示不会破坏对等约束且不会跨越主版本边界的更新。
锁文件和 CI 流水线集成
你的锁文件代表一个已知可工作的状态。相应地对待它。
在 CI 中,始终使用 npm ci 而不是 npm install。ci 命令在 package.json 和 package-lock.json 不同步时会失败——这正是你想要的。这可以捕获有人更新了 package.json 但忘记重新生成锁文件的情况。
在 CI 中自动化依赖更新工作流的模式如下:
ncu -u --target patch
npm install
npm test
如果测试通过,流水线提交更改。如果失败,更新会被标记为需要人工审查。
使用 Overrides 控制传递依赖
有时问题不在于你的直接依赖——而在于它们依赖的内容。依赖树中三层深处的漏洞需要更新一个你无法控制的包。
npm 的 overrides 字段允许你强制指定传递依赖的特定版本:
{
"overrides": {
"vulnerable-package": "2.0.1"
}
}
这是 JavaScript 包管理的标准部分。当你需要在上游维护者更新其依赖之前获得安全补丁时使用它。
结论
依赖更新自动化并不意味着不断更新所有内容。它意味着拥有一个可重复的流程,将发现与安装分离,尊重语义化版本边界,并保持锁文件与清单文件同步。
ncu 适合这个工作流,因为它将 package.json 更新视为一个独立的步骤。你决定升级什么、何时安装以及何时测试。该工具处理繁琐的部分——检查注册表和修改版本范围——同时将判断性决策留给你。
每周更新补丁。每月审查次版本。慎重处理主版本。保持锁文件已提交。你的 CI 会感谢你。
常见问题
npm update 在现有 package.json 版本范围内安装较新版本,但不会跨越主版本边界。npm-check-updates 修改 package.json 本身的版本范围,允许你更新到包括主版本在内的任何版本。ncu 更改清单文件,而 npm update 更改 node_modules。
可以。ncu 适用于任何包管理器,因为它只修改 package.json。无论你使用哪个包管理器,都可以使用 pnpm dlx npm-check-updates 或 npx npm-check-updates 运行它。在 ncu 更新 package.json 后,使用你首选的包管理器安装更新的依赖。
使用 reject 标志加上包名或模式。例如,ncu --reject typescript 从更新中排除 TypeScript。你也可以使用 ncu --reject '/react.*/' 排除匹配模式的多个包。这对于你想手动更新的包很有用。
运行 ncu -u 只修改 package.json 而不安装任何内容。风险来自之后在没有测试的情况下运行 npm install。在更新和安装后始终运行测试套件。使用 ncu -u --target patch 从补丁更新开始是最安全的方法。
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.