npm workspaces vs pnpm vs Lerna:从 sample-monorepo 看 monorepo 工具链怎么选
【免费下载链接】sample-monorepoSample monorepo setup with npm workspaces and typescript project references项目地址: https://gitcode.com/gh_mirrors/sa/sample-monorepo
选择 monorepo 工具链是前端与 Node.js 团队绕不开的决策题。本文以 sample-monorepo 这个「npm workspaces + TypeScript project references」的示例仓库为样本,用一篇文章讲清 npm workspaces、pnpm 与 Lerna 三者的定位差异、优缺点与适用场景,帮你快速确定自己的 monorepo 工具链怎么选。无论你是刚接触 monorepo 的新手,还是正在纠结迁移方案的老手,这份对比指南都能派上用场。
为什么 monorepo 越来越流行
一个项目往往由多个子包组成:组件库、业务应用、后端服务。传统多仓库(multi-repo)模式下,跨包改代码要发版、拉依赖、再联调,成本极高。而 monorepo 把多个包放进同一个仓库,带来三个核心收益:
- 🧩统一依赖:公共依赖只装一份,版本天然对齐
- ⚡跨包联调:改完组件库代码,应用立刻生效,无需发布
- 📦原子提交:一次提交可以同时包含多个包的改动,回溯更轻松
而要把 monorepo 跑起来,就需要一套称手的工具链。目前最主流的三个选项正是 npm workspaces、pnpm 和 Lerna。
sample-monorepo:一份可运行的 monorepo 教科书
在纸上谈兵之前,先看看 sample-monorepo 这个真实的示例仓库。它用最朴素的配置,演示了 monorepo 的标准姿势,非常适合当作学习样本。
仓库里都有什么
packages/ components/ # @sample/components React 组件库 app/ # @sample/app React 应用,依赖 components server/ # @sample/server Express 服务,依赖 app(含 SSR)三个包互相引用:app 依赖 components,server 又依赖 app,形成一条完整的依赖链。这正是 monorepo 最典型的用法——跨包协作就在仓库内部完成。
关键配置文件
- 根目录
package.json通过workspaces: ["packages/*"]声明工作区,这是 npm workspaces 的核心配置 - 根目录
tsconfig.json使用 references 引用三个子项目,配合tsconfig.base.json实现 TypeScript project references 增量构建 - 每个包自己的
packages/app/src/tsconfig.json、packages/server/src/tsconfig.json通过 extends 继承公共配置,再声明各自的 outDir 与 references lerna.json配置了npmClient: "npm"、useWorkspaces: true和version: "independent",负责版本发布- 整个仓库只有一个
package-lock.json,所有依赖统一锁版本
接下来,我们就围绕这份配置,逐一拆解三个工具的角色。
npm workspaces:开箱即用的工作区方案
npm workspaces 从 npm 7 起内置,不需要安装任何额外工具。sample-monorepo 选择它作为基础,正是看重其零门槛的特点。
npm workspaces 怎么用
只需在根package.json声明工作区目录,再执行npm install,npm 会自动完成三件事:
- 符号链接:把
packages/*下的本地包链接进根node_modules,跨包 import 无需发布 - 依赖提升:公共依赖 hoist 到根目录,避免重复安装
- 单一锁文件:全仓共用一份
package-lock.json,版本一致性好
运行子包脚本也很简单,比如启动 app 包:
npm run -w @sample/app startnpm workspaces 优缺点
✅ 优点:
- 零学习成本,npm 自带,文档多、踩坑少
- 与 CI、云平台等生态兼容性最好
- 配置极简,一个字段搞定
❌ 缺点:
- 安装速度一般,磁盘占用高于 pnpm
- 依赖提升带来的幽灵依赖(phantom dependency)问题较常见
- 新版严格的 workspaces 模式要求 Node 22+,老项目升级有成本
适合人群:中小团队、希望快速起步、不想引入额外依赖的项目。
pnpm workspaces:硬链接与内容寻址存储的极速派
如果 npm workspaces 是「够用」,那 pnpm 就是「更快、更省、更严格」的进阶选择。
pnpm 的核心优势
- 🚀内容寻址存储:所有依赖放进全局 store,各项目通过硬链接引用,同样的包只下载一次
- 💾磁盘占用极省:实测大型 monorepo 可节省 50% 以上的磁盘空间
- 🔒严格隔离:node_modules 结构更接近真实语义,只有 package.json 里声明的依赖才可见,从根上解决幽灵依赖
pnpm 同样支持 workspaces 配置,只需要把根目录的 workspaces 字段保留,安装命令换成pnpm install即可无缝迁移。像 sample-monorepo 这样的结构,改包管理器几乎零成本。
pnpm 的注意点
- 部分老旧工具链(个别 webpack 插件、electron 构建脚本)可能与严格的 node_modules 结构不兼容
- 团队需要适应
pnpm的命令习惯
适合人群:包数量多、依赖重、追求安装速度与磁盘效率的中大型团队。
Lerna:专注版本管理与发布的指挥官
很多人把 Lerna 和包管理器混为一谈,其实定位完全不同:npm/pnpm 负责「装依赖」,Lerna 负责「发版本」。
Lerna 解决什么问题
monorepo 里的包要分别发布到 npm,这就带来两个痛点:版本号怎么定、依赖关系怎么维护。Lerna 用两种模式解决:
- 固定模式(fixed):所有包共用一个大版本,发版时一起升
- 独立模式(independent):每个包各自维护版本号,谁改谁发
sample-monorepo 的lerna.json采用 independent 模式,配合 npm workspaces 使用:安装交给 npm,发布交给 Lerna。发布时执行:
npx lerna publishLerna 会自动识别哪些包自上次发布后有改动,询问新版本号,并联动更新依赖方。必要时可用npx lerna publish --force-publish强制全量发布。
Lerna 的定位
Lerna 本身不管理依赖安装,必须和 npm workspaces 或 pnpm 搭配使用。它更像 monorepo 工具链中的「发布指挥官」,负责最后一步的版本编排。
适合人群:需要把包发布到 npm、依赖关系复杂、对版本号管理有要求的开源项目与组件库团队。
三者对比一览表
| 维度 | npm workspaces | pnpm workspaces | Lerna |
|---|---|---|---|
| 本质 | 工作区机制 | 包管理器 + 工作区 | 版本与发布管理 |
| 依赖安装 | 内置,零依赖 | 内容寻址存储,极速 | 不负责安装 |
| 磁盘占用 | 一般 | 最省 | 不涉及 |
| 幽灵依赖 | 较常见 | 严格隔离 | 不涉及 |
| 版本发布 | 需自行处理 | 需自行处理 | 核心能力 |
| 学习成本 | 最低 | 中等 | 中等 |
| 适合场景 | 中小项目 | 中大型项目 | 需要发布 npm 的仓库 |
简单说:npm workspaces 管安装,pnpm 是更快的安装,Lerna 管发布,三者完全可以组合使用。
实操:用 sample-monorepo 跑通 monorepo 全流程
理论讲完,动手验证一遍才最有说服力。sample-monorepo 仓库可以直接克隆体验,执行以下命令:
git clone https://gitcode.com/gh_mirrors/sa/sample-monorepo cd sample-monorepo npm i npm run buildnpm i触发 workspaces 安装,三个包自动链接npm run build执行根目录的tsc --build,按 TypeScript project references 顺序增量构建:先 components,再 app,最后 server,依赖关系由各包的 references 自动推导
构建完成后,还可以体验两种运行方式:
npm start # 客户端 dev 模式,带 source-map npm run start:server # 服务端 SSR 模式,客户端先打包再渲染前后端共用一套仓库、一次安装、一次构建,这就是 monorepo 工具链带来的实际体验。
总结:monorepo 工具链怎么选
回到最初的问题,我的建议很简单:
- 🎯新手或小团队:直接选 npm workspaces,零成本起步,先把 monorepo 跑起来
- 🚀追求速度与磁盘效率:升级到 pnpm workspaces,配置迁移几乎无感
- 📦需要发布 npm 包:在 npm 或 pnpm 基础上叠加 Lerna,搞定版本管理
- 🏗️大型复杂仓库:可以考虑 Turborepo、Nx 等更重的构建编排方案,但那是另一个话题了
monorepo 工具链没有绝对的优劣,只有适不适合。像 sample-monorepo 这样先跑通最小闭环,再按需演进,才是最务实的路径。希望这份对比能帮你少走弯路,选对工具,把精力留给真正的业务开发。
【免费下载链接】sample-monorepoSample monorepo setup with npm workspaces and typescript project references项目地址: https://gitcode.com/gh_mirrors/sa/sample-monorepo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考