前端包管理工具 (npm/yarn/pnpm) 与 Monorepo
1. 面试题
Q: 请对比 npm, yarn, pnpm 的核心区别?为什么 pnpm 现在更受欢迎?
Q: 什么是 Monorepo?如何在项目中实践?
2. 核心解答:包管理工具进化史
(1) npm (Node Package Manager)
- v1/v2: 嵌套结构。依赖 A 用了 C(v1),依赖 B 用了 C(v2),node_modules 里就是
A/node_modules/C 和 B/node_modules/C。路径极深,且大量重复安装。
- v3+: 扁平化 (Flat) 结构。尝试把所有依赖都打平放在顶层
node_modules。
- 幽灵依赖 (Phantom Dependencies):依赖的依赖被提升到了顶层,导致你的项目可以直接
import 一个你package.json 里没写的包。这是不安全的。
- 不确定性:安装顺序不同可能导致扁平化结果不同 (虽然后来有了
package-lock.json)。
(2) yarn (v1)
Facebook 推出,为了解决 npm 安装慢、无锁文件导致版本不一致的问题。
- 特点:并行安装 (快),
yarn.lock (确定性),缓存机制。
- 结构:依然采用扁平化 node_modules,幽灵依赖问题依旧。
(3) pnpm (Performant NPM)
目前的版本之子。
- 策略:内容寻址存储 (Content-addressable store) + 硬链接 (Hard link) + 符号链接 (Symbolic link)。
- 全局存储:所有项目的依赖都物理存在于磁盘的某一个 Store 目录。项目中只是硬链接过去。磁盘空间极大节省,安装速度极快。
- 非扁平化:pnpm 的
node_modules 结构严格还原依赖树。
- 解决了 幽灵依赖:你只能 require 顶层
package.json 里声明的包。
- 解决了 分身 (Doppelgangers):不同版本的依赖精确隔离,不会重复打包。
3. Monorepo (单体仓库)
定义:在一个 Git 仓库中管理多个项目 (Packages/Apps)。
工具链:
- Workspaces (原声支持):
pnpm-workspace.yaml 或 package.json 的 workspaces 字段。定义哪些目录是子包。
- Lerna:早期的 Monorepo 管理工具,擅长版本发布 (Release) 和变更日志生成。现在多配合 nx 使用。
- TurboRepo / Nx:现代构建系统,支持任务缓存 (Caching) 和并行执行,极大提升 Monorepo 构建速度。
实践遇到的问题:
- 依赖版本不一致:Package A 用 React 16,Package B 用 React 18。
- 解法:在根目录使用
pnpm.overrides(或 yarnresolutions`) 强制锁定版本;或者使用 Syncpack 等工具检查版本一致性。
- 构建慢:改一行代码,所有包都要重编吗?
- 解法:使用 TurboRepo 根据依赖拓扑图构建,且利用缓存,没改过的包直接跳过。
4. 总结
- npm: Flat, Slow, Phantom Deps.
- yarn: Flat, Faster, Phantom Deps.
- pnpm: Symlinked, Fastest, Disk efficient, Strict.
- Monorepo: Multi-package management. Use pnpm workspace + TurboRepo.