pnpm 的 overrides 和 resolutions 有什么区别?如何使用?
pnpm.overrides:原生覆盖机制
pnpm.overrides 是 pnpm 原生的依赖覆盖配置,写在 package.json 的 pnpm 字段中,也可以写在 pnpm-workspace.yaml 里。
json// package.json { "pnpm": { "overrides": { "lodash": "^4.17.21", "react": "^18.0.0" } } }
yaml# pnpm-workspace.yaml overrides: "lodash": "^4.17.21" "react": "^18.0.0"
精确路径覆盖
用 > 指定只覆盖某个包的依赖,不影响其他包对同一依赖的使用:
json{ "pnpm": { "overrides": { "webpack>lodash": "^4.17.21", "antd>rc-util": "^5.30.0" } } }
也可以限定只覆盖特定版本的依赖:
json{ "pnpm": { "overrides": { "minimist@<1.2.6": "^1.2.6" } } }
引用项目直接依赖
用 $ 前缀引用项目自身声明的依赖版本,避免硬编码版本号导致不一致:
json{ "dependencies": { "react": "^18.2.0" }, "pnpm": { "overrides": { "react": "$react" } } }
这样所有间接依赖的 react 都会使用项目声明的 ^18.2.0,而不是单独写一个版本。
移除依赖
用 - 可以把某个依赖从依赖树中移除:
json{ "pnpm": { "overrides": { "some-package>unused-dep": "-" } } }
这在处理可选依赖或减少安装体积时有用。
替换包
将一个包替换为另一个:
json{ "pnpm": { "overrides": { "node-sass": "sass", "request": "axios" } } }
对 peerDependencies 生效
pnpm.overrides 会覆盖 peerDependencies 的版本解析。这在统一 React 版本时很常见——某些组件库的 peerDependency 声明了旧版 React,但你希望它们都使用项目中的版本。
resolutions:Yarn 兼容字段
resolutions 是 Yarn 的依赖覆盖字段,pnpm 为了方便从 Yarn 迁移的项目也支持读取它:
json{ "resolutions": { "lodash": "^4.17.21" } }
resolutions 的局限
resolutions 在 pnpm 中是兼容层,功能比 pnpm.overrides 少很多:
- 不支持
>路径指定,只能全局覆盖 - 不支持
$引用直接依赖 - 不支持
-移除依赖 - 不支持
@版本号限定范围 - 只能写在
package.json中,不支持pnpm-workspace.yaml
两者优先级
当 pnpm.overrides 和 resolutions 同时存在时,pnpm.overrides 优先级更高。如果同一条目两边都写了,以 pnpm.overrides 为准。
有个已知问题:当 pnpm-workspace.yaml 中写了 overrides 时,package.json 里的 resolutions 可能被忽略而不是合并。所以迁移项目时建议把 resolutions 手动迁移到 pnpm.overrides,而不是两边混用。
区别对比
| 特性 | pnpm.overrides | resolutions |
|---|---|---|
路径指定(>) | 支持 | 不支持 |
版本限定(@版本) | 支持 | 不支持 |
引用直接依赖($) | 支持 | 不支持 |
移除依赖(-) | 支持 | 不支持 |
| 配置位置 | package.json 或 pnpm-workspace.yaml | 仅 package.json |
| peerDependencies 覆盖 | 支持 | 支持 |
| 优先级 | 高 | 低 |
| 来源 | pnpm 原生 | Yarn 兼容 |
什么时候用哪个
新项目直接用 pnpm.overrides,不需要考虑 resolutions。
从 Yarn 迁移的项目,短期可以保留 resolutions 让项目先跑起来,但应尽快迁移到 pnpm.overrides,否则缺少路径指定和版本限定功能,覆盖精度不够,还可能遇到优先级冲突问题。
常见使用场景
修复传递依赖的安全漏洞
第三方包依赖了有漏洞的旧版本,该包还没更新,你先手动覆盖:
json{ "pnpm": { "overrides": { "follow-redirects@<1.15.6": "1.15.6" } } }
统一 React 版本
monorepo 里不同包可能依赖不同版本的 React,用 $ 引用统一:
json{ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0" }, "pnpm": { "overrides": { "react": "$react", "react-dom": "$react-dom" } } }
只覆盖某个包的依赖
某个包依赖了不兼容的版本,但你不想影响其他包:
json{ "pnpm": { "overrides": { "antd>rc-util": "^5.30.0" } } }
验证覆盖效果
修改 overrides 后需要重新安装依赖:
bashpnpm install
用以下命令确认覆盖是否生效:
bash# 查看实际安装的版本 pnpm list react # 查看某个依赖被谁引入 pnpm why lodash # 查看完整依赖树 pnpm list --depth=10
注意事项
全局覆盖要谨慎。把所有包的某个依赖强制升到大版本,可能导致不兼容。优先用路径覆盖(>)只影响目标包。
修改 overrides 后记得重新 pnpm install,如果锁文件没更新,可以 pnpm install --force 强制刷新。