前端包管理工具 (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/CB/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)。

工具链

  1. Workspaces (原声支持)pnpm-workspace.yamlpackage.jsonworkspaces 字段。定义哪些目录是子包。
  2. Lerna:早期的 Monorepo 管理工具,擅长版本发布 (Release) 和变更日志生成。现在多配合 nx 使用。
  3. 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.