雾里挑灯看剑️头像

Pnpm Monorepo 理论与实战指南

PNPM 是现代 Node 包管理器,Monorepo 是「单仓库多包」的工程架构;pnpm-workspace.yaml 是 PNPM Monorepo 的核心配置文件。workspace:* 用于引用仓库内本地源码包;catalog 是 PNPM9+ 专属全局版本池,统一所有子应用第三方依赖版本。本文包含基础理论、配置详解、常用命令、踩坑经验,可日常查阅。

一、基础概念区分

1. Monorepo 是什么

Monorepo 是代码仓库组织架构:一个 Git 仓库,同时维护多个独立 package 包。 包可以是:公共工具库、组件库、前端业务应用。

  • 优势:
    1. 内部包修改实时生效,无需打包发布到 npm
    2. 统一代码规范、CI 流水线、版本管理
    3. 内部包之间互相依赖,本地调试非常方便

注意:Monorepo ≠ PNPM。Monorepo 是工程思想;pnpm/yarn/npm 是包管理器,都可以实现 monorepo。

2. PNPM 是什么

PNPM 是 Node 的包管理器,对比 npm/yarn:

  1. 全局硬链接存储,相同包只下载一次,节省磁盘空间
  2. 依赖扁平化策略更严格,幽灵依赖问题更少
  3. 原生内置 Workspace 能力,对 monorepo 项目友好
  4. PNPM9 新增 catalog 版本池,专门优化多子包场景下的依赖版本统一管理

3. pnpm-workspace.yaml

monorepo 项目核心配置文件,必须放在仓库根目录。 作用:告诉 pnpm 两件核心事:

  1. 哪些目录属于当前 workspace 工作区(哪些文件夹算作子包)
  2. 【可选】catalog 版本池,统一管理第三方 npm 包版本

二、pnpm-workspace.yaml 完整配置说明

最小可用模板

# pnpm-workspace.yaml
packages:
  - 'packages/*' # packages 下一级目录全部纳入workspace(存放公共源码包)
  - 'apps/*'     # apps 下一级目录全部纳入workspace(存放前端应用)

# catalog:默认版本池,pnpm9+专属,统一第三方依赖版本
catalog:
  "@tanstack/vue-query": "^5.59.0"
  dayjs: "^1.11.13"

# 多版本池子(可选,同一个包维护多套版本)
# catalogs:
#   vue34:
#     vue: "^3.4.30"
#   vue35:
#     vue: "^3.5.8"

# 可选配置项
disallowWorkspaceCycles: true # 禁止循环依赖,出现循环依赖直接install报错
saveWorkspaceProtocol: rolling # 添加本地workspace包依赖时,自动写入 workspace:*
includeWorkspaceRoot: false # 递归执行命令,默认不在根包执行

1. packages 字段详解(glob 通配符)

packages 使用 glob 匹配规则:匹配到的目录,只要包含 package.json,就会被识别为 workspace 子包,子包之间可以使用 workspace:* 协议互相引用。

表格

glob 写法含义
packages/*匹配 packages 下一级子文件夹,不会递归深层目录
apps/**递归匹配 apps 下所有层级目录(多层嵌套)
!**/test/**! 代表排除,所有 test 目录下的包不纳入 workspace

只有被 packages 匹配到的目录,才能使用 workspace:* 协议;不在列表中的目录,无法引用本地源码包。

2. workspace:* 协议

// apps/app-one/package.json
"@common/design": "workspace:*"
  • 归属:pnpm/yarn/npm 均支持,属于 monorepo 通用约定
  • 含义:不要去 npm 远程仓库下载,直接引用当前 workspace 内本地源码包,pnpm 创建软链接。
  • *:直接匹配本地包当前版本,不做版本号校验。
  • 使用场景:仓库内部公共源码包,放在 packages 目录下。

适用:仓库内部源码包;业务第三方 npm 包不要使用 workspace:*。

3. catalog 版本池(pnpm 9+ 专属)

# pnpm-workspace.yaml根配置
catalog:
  dayjs: "^1.11.13"

子应用 package.json 中:

"dayjs": "catalog:"
  • 归属:PNPM 独有,yarn/npm 不识别该语法。
  • 含义:版本号从根目录 catalog 池读取,所有子包共用同一个版本。
  • 优势:升级依赖,只修改 yaml 一处,不用修改 apps、packages 下大量 package.json。

⚠️ 规范:业务新增第三方包不要加到 catalog。catalog 适合框架底层公共依赖维护,业务组件直接写固定版本号。

三、依赖安装两种模式

区分:安装到子应用 vs 安装到workspace 根目录

方式 1:安装到子应用(推荐:仅单个 app 使用的依赖)

场景:依赖只在 apps/app-one 使用,其它子应用不需要,例如拖拽分割面板组件

# 进入子应用目录
cd apps/app-one
# 安装依赖,写入 apps/app-one/package.json
pnpm add vue-splitpanes

特点:

  1. 依赖隔离,其它子应用打包不会包含这个包
  2. 删除该子应用时,依赖同步清理,无冗余
  3. 不改动根 package.json,不改动 pnpm-workspace.yaml

方式 2:安装到 workspace 根目录(-w 参数)

场景:多个子应用(app-one、app-two)都要复用这个依赖

# 在项目仓库根目录执行(和pnpm-workspace.yaml同级)
pnpm add vue-splitpanes -w
  • -w = --workspace-root,依赖写入仓库根 package.json 特点:
  1. 全仓库所有子包都可以 import,仓库内只保留一份依赖
  2. 缺点:所有子应用打包都会带上该包,增大不需要该包的应用打包体积
  3. 卸载:根目录执行 pnpm remove vue-splitpanes -w

四、PNPM Workspace 高频常用命令

全部在仓库根目录执行

1. 查看所有 workspace 子包

pnpm ls -r

2. 递归所有子包执行命令 -r / --recursive

# 所有子包install
pnpm -r install

# 所有子包执行build构建
pnpm -r build

3. filter:筛选指定子包执行(高频)

# 只对 app-one 执行 build
pnpm --filter app-one build

# 只对 app-one 执行安装依赖
pnpm --filter app-one add xxx

4. 新增 / 删除依赖

# 根安装
pnpm add xxx -w
# 根卸载
pnpm remove xxx -w

# 子包安装(先cd进apps/app-one)
pnpm add xxx
pnpm remove xxx

5. 查看 workspace 依赖树

pnpm ls

五、概念对比表(快速查阅)

表格

标识 / 配置提供方作用使用场景
workspace:*pnpm/yarn/npm 均支持引用仓库本地源码包,软链接,不从 npm 下载仓库内部 packages 公共源码包
catalog:pnpm9 + 独有统一第三方 npm 包版本,版本定义在 pnpm-workspace.yaml底层公共第三方依赖,业务新增包不要用
根 package.jsonpnpm存放全仓库共享依赖多个子应用共用的包,使用pnpm add -w
子应用 package.jsonpnpm存放当前 app 私有依赖仅单个应用使用的业务组件、第三方库

六、实战经验 & 避坑

  1. 不要手动修改 catalog:catalog 用于底层公共依赖管理,业务新增第三方包直接在子应用pnpm add,写固定版本号,不要写catalog:。
  2. workspace:* 只用于本地 packages 源码包,不要用来引用 npm 远程包。
  3. packages 数组不要随意修改:删除apps/*会导致所有 app 脱离 workspace,workspace:*全部失效。
  4. 依赖安装原则:哪个 app 用,就装到哪个 app;只有多 app 共用,才放到根目录使用-w。
  5. 循环依赖:disallowWorkspaceCycles: true开启后,A 依赖 B,B 依赖 A,pnpm install直接报错,需要重构代码。
  6. 包导入路径:子包内部使用@别名,是子应用内 tsconfig/vite 配置,不是 pnpm workspace 能力。
  7. 安装第三方 UI 组件,记得手动引入组件 css,否则组件无样式。

七、FAQ

Q1:monorepo 和 pnpm 是一回事吗?

A:不是。Monorepo 是仓库架构;pnpm 是包管理器。pnpm 的 workspace 是 pnpm 实现 monorepo 的一套能力。

Q2:catalog 是不是必须要用?

A:不是,catalog 是 pnpm9 的增强特性,monorepo 项目完全可以不使用,每个子 package.json 写独立版本号。catalog 只是用来简化多包项目的版本维护。

Q3:pnpm add -w 安装的包,可以在子应用直接 import 吗?

A:可以。所有 workspace 下的子包都能导入根 package.json 里的依赖。代价是其它子应用打包也会包含该依赖。

Q4:新增自定义业务 Vue 组件,放在哪里?

A:业务组件源码放在子应用内部,例如 apps/app-one/src/components/biz,不属于 pnpm package,不需要在 pnpm-workspace.yaml 注册,只是普通 vue 文件。

Q5:升级仓库公共包,会不会影响 workspace 配置?

A:升级 packages 内公共源码包,一般不会改动pnpm-workspace.yaml,自己新增的业务依赖不受影响。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/jwbabc/article/details/168240252

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--