前言
依赖管理是 Monorepo 架构中的核心问题之一。如何正确配置依赖关系,避免重复安装,确保类型安全,是每个 Monorepo 项目必须解决的问题。
本文以 Vue Ace Admin 项目为例,深入解析 Monorepo 中的依赖管理策略。
如果你还不熟悉 Monorepo 基础配置,建议先阅读 从零搭建 Monorepo 项目。
workspace 协议详解
什么是 workspace 协议
workspace:* 是 pnpm 提供的特殊协议,用于引用本地 workspace 中的包。
基本用法:
json
{
"dependencies": {
"@codexlin/ace-admin-hooks": "workspace:*",
"@codexlin/ace-admin-ui": "workspace:*"
}
}
workspace 协议的工作原理
本地开发时:
bash
# pnpm 自动创建符号链接
node_modules/@codexlin/ace-admin-hooks -> packages/hooks
node_modules/@codexlin/ace-admin-ui -> packages/ui
发布到 npm 时:
bash
# pnpm 自动替换为实际版本号
"@codexlin/ace-admin-hooks": "workspace:*"
# 发布后变为
"@codexlin/ace-admin-hooks": "^1.0.0"
workspace 协议的优势
-
开发效率
- 直接使用源码,修改立即生效
- 无需构建即可使用
- 完整的 TypeScript 类型支持
-
无缝切换
- 本地开发和 npm 使用方式完全一致
- 无需修改代码即可切换
-
版本同步
- 自动使用最新版本
- 避免版本不一致问题
依赖类型详解
dependencies - 运行时依赖
使用场景: 包运行时需要的依赖
示例:
json
// packages/ui/package.json
{
"dependencies": {
"ant-design-vue": "~4.2.6"
}
}
特点:
- 会被安装到
node_modules - 会被打包到最终产物中(除非 external)
- 版本会被锁定
devDependencies - 开发依赖
使用场景: 只在开发时需要的依赖
示例:
json
// packages/ui/package.json
{
"devDependencies": {
"vite": "^6.0.0",
"typescript": "~5.9.0",
"vue": "^3.5.0"
}
}
特点:
- 只在开发时安装
- 不会被打包到最终产物
- 不会传递给使用该包的项目
peerDependencies - 对等依赖
使用场景: 期望宿主环境提供的依赖
示例:
json
// packages/ui/package.json
{
"peerDependencies": {
"vue": ">=3.4.0",
"ant-design-vue": ">=4.0.0"
}
}
特点:
- 不会自动安装
- 需要宿主环境提供
- 避免重复安装,减小包体积
- 版本要求更灵活
为什么使用 peerDependencies?
typescript
// 场景:UI 包依赖 Vue
// ❌ 不使用 peerDependencies
// 每个使用 UI 包的项目都会安装 Vue
// 导致多个 Vue 实例,体积增大
// ✅ 使用 peerDependencies
// Vue 由主应用提供,所有包共享同一个 Vue 实例
// 减小包体积,避免版本冲突
optionalDependencies - 可选依赖
使用场景: 依赖不存在时不影响功能
示例:
json
{
"optionalDependencies": {
"some-optional-package": "^1.0.0"
}
}
依赖提升机制
pnpm 的依赖提升策略
pnpm 使用符号链接和硬链接来管理依赖,避免重复安装:
text
node_modules/
├── .pnpm/
│ ├── vue@3.5.13/
│ │ └── node_modules/
│ │ └── vue
│ └── ant-design-vue@4.2.6/
│ └── node_modules/
│ └── ant-design-vue
│
├── vue -> .pnpm/vue@3.5.13/node_modules/vue
└── ant-design-vue -> .pnpm/ant-design-vue@4.2.6/node_modules/ant-design-vue
优势:
- 节省磁盘空间
- 安装速度快
- 版本管理清晰
依赖提升配置
根目录 .npmrc:
text
# 提升所有依赖到根目录
shamefully-hoist=true
# 提升 peerDependencies
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
使用场景:
- 某些工具需要依赖在根目录(如 ESLint、Prettier)
- 避免每个包都安装相同的开发工具
版本管理策略
版本号规范
语义化版本(SemVer):
major.minor.patchmajor:不兼容的 API 变更minor:向后兼容的功能新增patch:向后兼容的问题修复
示例:
json
{
"version": "1.0.0" // hooks 包
"version": "0.3.0" // ui 包(仍在开发中)
}
版本同步策略
策略一:独立版本(推荐)
每个包独立管理版本:
json
// packages/hooks/package.json
{ "version": "1.0.0" }
// packages/ui/package.json
{ "version": "0.3.0" }
优势:
- 版本管理灵活
- 减少不必要的版本更新
- 用户可以选择性安装
策略二:统一版本
所有包使用相同版本:
json
// 使用脚本同步版本
{
"scripts": {
"version:sync": "node scripts/sync-version.js"
}
}
使用场景:
- 包之间紧密耦合
- 需要保持版本一致
实际案例:Vue Ace Admin 依赖配置
Hooks 包配置
json
// packages/hooks/package.json
{
"name": "@codexlin/ace-admin-hooks",
"peerDependencies": {
"vue": "^3.0.0" // Vue 由宿主环境提供
},
"devDependencies": {
"vue": "^3.5.0", // 开发时使用
"typescript": "^5.0.0", // 类型检查
"vite": "^6.0.0" // 构建工具
}
}
设计思路:
peerDependencies:Vue 由使用方提供devDependencies:开发工具不传递给使用者
UI 包配置
json
// packages/ui/package.json
{
"name": "@codexlin/ace-admin-ui",
"peerDependencies": {
"vue": ">=3.4.0",
"ant-design-vue": ">=4.0.0"
},
"devDependencies": {
"vue": "^3.5.0",
"ant-design-vue": "~4.2.6",
"typescript": "~5.9.0",
"vite": "^6.0.0"
}
}
设计思路:
peerDependencies:Vue 和 Ant Design Vue 由宿主提供devDependencies:开发时使用完整版本进行测试
主应用配置
json
// package.json(根目录)
{
"dependencies": {
"@codexlin/ace-admin-hooks": "workspace:*",
"@codexlin/ace-admin-ui": "workspace:*",
"vue": "~3.5.13",
"ant-design-vue": "~4.2.6"
}
}
依赖关系图:
text
主应用
├── vue@3.5.13 (实际安装)
├── ant-design-vue@4.2.6 (实际安装)
├── @codexlin/ace-admin-hooks (workspace,使用主应用的 vue)
└── @codexlin/ace-admin-ui (workspace,使用主应用的 vue 和 ant-design-vue)
依赖管理最佳实践
1. 合理使用 peerDependencies
何时使用:
- 框架类依赖(Vue、React)
- UI 库依赖(Ant Design Vue、Element Plus)
- 大型依赖(避免重复安装)
示例:
json
{
"peerDependencies": {
"vue": ">=3.4.0" // ✅ 框架依赖
},
"dependencies": {
"lodash-es": "^4.17.21" // ✅ 工具库,可以打包
}
}
2. workspace 包之间的依赖
Hooks 包 → UI 包:
json
// packages/ui/package.json
{
"dependencies": {
"@codexlin/ace-admin-hooks": "workspace:*"
}
}
主应用 → 两个包:
json
// package.json
{
"dependencies": {
"@codexlin/ace-admin-hooks": "workspace:*",
"@codexlin/ace-admin-ui": "workspace:*"
}
}
3. 版本范围策略
开发依赖: 使用精确版本或小范围
json
{
"devDependencies": {
"typescript": "~5.9.0", // 允许补丁版本更新
"vite": "^6.0.0" // 允许小版本更新
}
}
运行时依赖: 根据兼容性选择
json
{
"dependencies": {
"vue": "~3.5.13", // 锁定小版本
"ant-design-vue": "~4.2.6" // 锁定小版本
}
}
4. 依赖更新策略
定期更新:
bash
# 检查过时的依赖
pnpm outdated
# 更新依赖
pnpm update
# 更新特定包
pnpm update vue@latest
安全更新:
bash
# 使用 pnpm audit 检查安全漏洞
pnpm audit
# 自动修复
pnpm audit --fix
常见问题与解决方案
Q1: peerDependencies 警告
问题: 安装时出现 peerDependencies 警告
text
WARN Issues with peer dependencies found
└─ ant-design-vue@4.2.6
└─ ✕ missing peer vue@"^3.4.0"
解决方案:
bash
# 安装缺失的 peer 依赖
pnpm add vue@^3.4.0
# 或使用 --shamefully-hoist(不推荐)
pnpm install --shamefully-hoist
Q2: 版本冲突
问题: 不同包需要不同版本的依赖
解决方案:
- 使用 peerDependencies:让宿主环境决定版本
- 版本范围:使用兼容的版本范围
- 依赖提升:pnpm 自动处理版本冲突
Q3: workspace 包无法解析
问题: TypeScript 无法解析 workspace 包
解决方案:
json
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@codexlin/ace-admin-hooks": ["./packages/hooks/src"],
"@codexlin/ace-admin-ui": ["./packages/ui/src"]
}
}
}
总结
Monorepo 的依赖管理需要平衡多个因素:
- workspace 协议:实现本地开发和 npm 发布的无缝切换
- peerDependencies:避免重复安装,减小包体积
- 依赖提升:pnpm 的符号链接机制优化安装效率
- 版本管理:合理的版本策略确保兼容性
通过合理的依赖配置,可以实现:
- ✅ 更小的包体积
- ✅ 更快的安装速度
- ✅ 更好的类型安全
- ✅ 更灵活的版本管理
下一步,你可以学习:
如果你对 Vue 3 企业级工程实践 感兴趣,可以查看我们的 架构实践分类 下的其他文章。