NPM

Yarn 与 pnpm:其他包管理工具

了解 Yarn 和 pnpm 与 NPM 的区别,掌握它们的基本命令和适用场景,学会根据项目选择合适的包管理工具。

🎯 引言

学会这篇文章,你将知道除了 NPM 之外,前端开发中常用的 Yarnpnpm 是什么,它们各自解决了什么问题,以及如何在不同项目中做出合适的选择。


🧱 为什么需要其他包管理工具

NPM 是 Node.js 自带的包管理工具,功能完善,生态成熟。但随着项目变大、依赖变多,大家也发现了一些痛点:

  • 安装速度慢:NPM 早期版本串行下载依赖,大项目安装很耗时。
  • 磁盘占用大:每个项目的 node_modules 都独立保存一份相同的包,重复文件很多。
  • 依赖结构不确定:不同时间执行 npm install,可能会得到略有不同的依赖树。

Yarn 和 pnpm 就是为了解决这些问题而生的替代方案。它们和 NPM 共用同一个包仓库(npm registry),所以包的来源是一样的,只是安装和管理方式不同。


✨ Yarn

Yarn 是 Facebook 于 2016 年推出的包管理工具,目标是解决当时 NPM 安装慢、不稳定的问题。

主要特点

  • 并行下载:多个包同时下载,速度比早期 NPM 快很多。
  • 锁文件 yarn.lock:锁定依赖精确版本,保证团队安装一致。
  • 离线模式:下载过的包会缓存,断网时也能复用。
  • 工作区(Workspaces):方便管理 monorepo(一个仓库多个包)。

常用命令

# 安装项目全部依赖
yarn install

# 安装指定包
yarn add dayjs
yarn add dayjs@1.11.13

# 安装开发依赖
yarn add eslint -D

# 卸载包
yarn remove dayjs

# 运行脚本
yarn dev
yarn build
Yarn 的命令比 NPM 更简洁,比如 yarn dev 等价于 npm run devyarn add 等价于 npm install

⚡ pnpm

pnpm 是近年来非常流行的包管理工具,名字代表 performant npm(高性能 NPM)。它在速度、磁盘占用和依赖管理严格性上做了大量优化。

主要特点

  • 内容可寻址存储:所有包只在本机保存一份,不同项目通过硬链接共用,极大节省磁盘空间。
  • 非扁平 node_modules:默认使用严格的依赖树结构,避免“幽灵依赖”问题。
  • 安装速度快:由于大量复用本地缓存,重复安装时速度非常快。
  • 原生 workspaces 支持:内置 monorepo 多包管理能力。

常用命令

# 安装项目全部依赖
pnpm install

# 安装指定包
pnpm add dayjs
pnpm add dayjs@1.11.13

# 安装开发依赖
pnpm add eslint -D

# 卸载包
pnpm remove dayjs

# 运行脚本
pnpm dev
pnpm build

🪤 什么是幽灵依赖

幽灵依赖(Phantom Dependencies)是指:你的代码里直接 importrequire 了一个包,但这个包并没有写在你的 package.json 里,而是被其他依赖“顺便”带进来的。

举个例子

假设你的项目只显式安装了 element-plus

package.json
{
    "dependencies": {
        "element-plus": "^2.9.0"
    }
}

element-plus 内部依赖了 vue@popperjs/core。在 NPM 或 Yarn 的扁平 node_modules 结构下,这些间接依赖会被提升到 node_modules 的顶层:

node_modules/
├─ element-plus/
├─ vue/          ← 被提升上来了
└─ @popperjs/core/  ← 也被提升上来了

于是你的代码可以这样写,并且能正常运行:

main.js
import { createApp } from 'vue'; // ⚠️ vue 不在 package.json 里!

问题就出在这里:

  1. 你的 package.json 里没有 vue,但代码却依赖了它。
  2. 一旦 element-plus 升级后不再依赖 vue,或者换成别的版本,你的代码就会直接报错。
  3. 换一台电脑、换一个包管理工具,依赖树的提升结果可能不同,导致“我电脑上能跑,你电脑上不能跑”。

这种“看不见、管不住”的依赖,就是幽灵依赖。


🛡 pnpm 如何解决幽灵依赖

pnpm 默认采用非扁平的 node_modules 结构,只把你在 package.json 里显式声明的包放到顶层:

node_modules/
├─ element-plus/  ← 只有你自己安装的包
└─ .pnpm/         ← 其他依赖被严格隔离在这里
    ├─ vue@3.x/
    └─ @popperjs/core@x.x/

在这种结构下,如果你试图 import vue,pnpm 会直接告诉你找不到这个模块,因为 vue 不在顶层。

pnpm 默认会严格管理依赖。如果 A 包依赖 B 包,你在代码里不能直接 import B,除非你自己也安装了 B。这种设计能避免很多隐式依赖问题。

如果你想在项目中使用 vue,就必须显式安装:

pnpm add vue

这样 vue 会写入 package.json,你的依赖关系就变得清晰、可控,不会再出现幽灵依赖。


🧰 什么是 Workspaces 和 Monorepo

Monorepo 是指把多个相关项目或包放在同一个 Git 仓库里管理。比如一个组件库仓库里同时包含:

  • packages/ui:UI 组件源码
  • packages/utils:工具函数
  • apps/docs:文档站点

Workspaces 就是包管理工具提供的“多包管理”能力。开启 workspaces 后,你可以在仓库根目录执行一次安装,同时管理多个子包的依赖,子包之间还可以互相引用。

pnpm workspaces 示例

在仓库根目录创建 pnpm-workspace.yaml

pnpm-workspace.yaml
packages:
    - 'packages/*'
    - 'apps/*'

然后执行:

pnpm install

pnpm 会自动识别 packagesapps 下的所有子项目,统一安装依赖。相比 Yarn,pnpm 的 workspaces 与它的存储机制结合得更好,安装速度更快、磁盘占用更小。

对于只有单个前端应用的项目,不需要关心 workspaces。当你开始维护组件库、工具库或多个相互依赖的子项目时,workspaces 才会派上用场。

⚖️ NPM、Yarn、pnpm 对比

特性NPMYarnpnpm
出品方Node.js 官方Facebook(现 Meta)社区开源
安装速度较快(v5 以后改进明显)很快
磁盘占用小(内容可寻址存储)
锁文件package-lock.jsonyarn.lockpnpm-lock.yaml
依赖结构扁平扁平非扁平,更严格
幽灵依赖问题容易出现容易出现基本避免
workspaces支持支持原生支持,效率更高
推荐场景通用,Node.js 默认自带大项目、monorepo追求速度、严格依赖管理、monorepo
三种工具访问的是同一个 NPM 仓库,包本身没有区别。选择哪个工具,主要看团队习惯、项目规模和对依赖管理严格程度的要求。

💡 如何选择

选 NPM 的情况

  • 你是初学者,不想额外安装工具。
  • 项目规模不大,NPM 已经完全够用。
  • 团队统一使用 NPM,避免工具混用。

选 Yarn 的情况

  • 项目依赖很多,希望安装速度更快。
  • 团队已经在用 Yarn,保持统一。
  • 需要使用 Yarn 的 workspaces 管理 monorepo。

选 pnpm 的情况

  • 电脑磁盘空间有限,希望节省空间。
  • 项目依赖复杂,希望避免幽灵依赖问题。
  • 团队追求更快的安装速度和更严格的依赖管理。
一个项目最好只使用一种包管理工具。不要混用 package-lock.jsonyarn.lockpnpm-lock.yaml,否则会造成依赖不一致。

🛠 在项目中切换工具

如果你接手了一个用 pnpm 的项目,但电脑上没有安装 pnpm,可以先全局安装:

npm install pnpm -g

当前课程环境使用的 pnpm 版本为 10.6.2,你可以在安装时指定版本以保证一致:

npm install pnpm@10.6.2 -g

安装完成后,用 pnpm -v 检查版本,然后就可以使用 pnpm install 安装项目依赖了。


🧾 小节总结

  • NPM、Yarn、pnpm 都基于同一个 NPM 仓库,只是安装和管理策略不同。
  • Yarn 以并行安装和 yarn.lock 著称,适合大项目和 monorepo。
  • pnpm 以内容可寻址存储和严格依赖树著称,能避免幽灵依赖,速度快、磁盘占用小。
  • workspaces 是管理 monorepo 多包的能力,pnpm 在这方面原生支持更好。
  • 选择工具要看团队习惯和项目需求,一个项目不要混用多种锁文件。

❓ 知识问答

Q1:Yarn 和 pnpm 能安装 NPM 仓库里的包吗?

A:可以。它们都兼容 NPM 仓库,包的来源完全相同。

Q2:yarn.lockpnpm-lock.yaml 需要提交到 Git 吗?

A:需要。它们和 package-lock.json 作用一样,都是用来锁定依赖版本的。

Q3:pnpm 为什么能节省磁盘空间?

A:pnpm 把所有下载过的包放在全局存储中,不同项目通过硬链接共用同一份文件,而不是每个项目都复制一份。

Q4:什么是幽灵依赖?

A:幽灵依赖是指代码里直接使用了某个包,但这个包并没有写在你的 package.json 里,而是通过其他依赖被“顺便”安装进来的。一旦那个间接依赖升级或移除,你的代码就会报错。

Q5:pnpm 为什么能避免幽灵依赖?

A:pnpm 使用非扁平的 node_modules 结构,只有 package.json 里显式声明的包才能被直接引用。没有被显式安装的包无法被代码直接 import

Q6:一个项目可以同时用 NPM 和 pnpm 吗?

A:不建议。混用会导致锁文件不一致,团队成员安装的依赖版本可能不同。


🧪 小练习

  1. 查看你当前电脑是否安装了 Yarn 或 pnpm:
# 请在这里输入命令
  1. 尝试用 pnpm 安装 dayjs@1.11.13
# 请在这里输入命令
  1. 观察项目根目录,看看生成了哪个锁文件。

🎉 恭喜你了解了 Yarn 和 pnpm!至此,NPM 课程已经全部学完。你可以根据项目需求,灵活选择合适的包管理工具了。