关于npm和pnpm
1. npm
(传统包管理器)
工作原理:
扁平化依赖:安装依赖时,
npm
会尽量将子依赖提升到node_modules
顶层,避免深层嵌套。例如:安装vue@3.2.0
时,它的子依赖@vue/reactivity
会被放在node_modules/@vue/reactivity
,而不是node_modules/vue/node_modules/@vue/reactivity
。独立存储:每个项目的
node_modules
都是完全独立的副本,即使多个项目用同一版本的库,也会重复下载存储。
优点:
简单易用,Node.js 自带,无需额外安装。
扁平化结构让开发者能直接引用子依赖(即使未显式声明)。
缺点:
磁盘空间浪费:100 个项目用
vue
,就有 100 份vue
文件。依赖不确定性:扁平化可能导致版本冲突(如两个依赖需要不同版本的
axios
)。幽灵依赖(后文详解)。
2. pnpm
(高效包管理器)
工作原理:
全局存储 + 硬链接:
所有依赖的包实际存储在统一的全局目录(如
~/.pnpm-store
)。项目的
node_modules
中只创建这些文件的硬链接(几乎不占额外空间)。
嵌套依赖结构:
严格保持依赖树的嵌套关系,避免扁平化。
例如
vue
的子依赖@vue/reactivity
会放在node_modules/vue/node_modules/@vue/reactivity
。
优点:
极快安装:依赖已存在时,直接创建硬链接(秒级安装)。
节省磁盘:100 个项目用
vue
,磁盘上只有 1 份vue
文件。严格依赖隔离:避免版本冲突和幽灵依赖。
缺点:
需要单独安装(
npm install -g pnpm
)。某些老旧工具可能不兼容嵌套的
node_modules
结构。
3. 幽灵依赖
幽灵依赖是指:项目中使用了未在 package.json
的 dependencies
或 devDependencies
中声明的包,但这些包却能正常工作(直到某天突然崩溃)。
npm的扁平化依赖是罪魁祸首:假设你安装 A包,A依赖 B@1.0。npm会把 B@1.0提升到 node_modules顶层。你的代码直接引用 B(即使没在 package.json声明),也能运行!这就是一个“幽灵依赖”。