ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

npm、yarn与pnpm全面对比:依赖安装、锁文件与Monorepo实践

npm、yarn与pnpm全面对比:依赖安装、锁文件与Monorepo实践 1. 安装速度与磁盘占用第一眼就能看到的差别第一次意识到这三个包管理器不是同一种东西换个马甲是我帮朋友迁移一个中等规模的 SSR 项目。项目本身不复杂Express 一套前端构建链依赖大概 400 个包。朋友之前一直用 npm装完node_modules接近 900MB那天我手头装了 pnpm直接在同一个目录里跑了pnpm install装完后看磁盘占用新增只有 300MB 出头。他当场问了一句它是不是偷偷没装全当然不是。这个问题的根源就是 npm、pnpm、yarn 在安装策略和存储机制上的巨大分歧。理解这三者的差异与其去背功能对比表不如先把一个包到底是怎么落到磁盘上的搞清楚。1.1 同一个项目三种工具装完的体感差距我拿一个真实的项目做过对比。项目是一个全栈内部管理系统package.json里大约 80 个直接依赖算上传递依赖总共 500 多个包。三台同样的机器、同样的 Node 版本、干净缓存冷安装一次。包管理器node_modules 体积安装耗时冷启动二次安装耗时npm约 850MB约 90 秒约 30 秒yarn (v1)约 820MB约 70 秒约 20 秒pnpm磁盘增量约 300MB约 60 秒约 5 秒这个数据不是权威基准测试但基本符合我长期使用的体感。npm 和 yarn v1 在占用空间这一项上属于同一量级而 pnpm 的磁盘占用和二次安装速度几乎可以用惊艳来形容。但要注意pnpm 的 300MB 是新增占用——因为很多包已经在全局 store 里了通过硬链接直接复用并不需要真正复制一份文件。如果是全新机器、没有任何缓存pnpm 的首装时间和 npm 差距没这么大但在存储上依然有优势。1.2 硬链接与全局存储pnpm 的磁盘魔法pnpm 的核心设计可以概括成三句话所有包都下载到一个**全局内容寻址存储content-addressable store**中一般默认在用户目录下的.pnpm-storeWindows 上是%LOCALAPPDATA%\pnpm\storemacOS/Linux 是~/.pnpm-store。项目里的node_modules只是这个 store 的视图。包与包之间通过硬链接共享同一个物理文件不复制数据。每个项目都有一层严格的符号链接结构来还原真实的依赖树。用一个生活化的类比store 就像城市图书馆的总书库每个项目里的node_modules不是把你需要的书重新买一份摆到家里而是给每一本借出来的书办一张借阅卡——卡片指向总书库里那本真实存在的书。你改不了总书库的书所以 pnpm 会把包文件设为只读但你家里确实有这本书可读。硬链接在不同操作系统上的表现有细节差异同一磁盘分区内硬链接几乎不占额外空间如果是跨分区比如 store 在 C 盘、项目在 D 盘pnpm 会警告你无法建立硬链接只能回退到复制文件这时候空间优势会大幅缩水速度也会受影响。这也是很多 Windows 用户安装 pnpm 后觉得并没有想象中那么省空间的原因——默认 store 位置和项目位置经常不在同一个盘。1.3 yarn 的两次转型从扁平依赖到 PlugnPlayyarn 的历史比较有意思。yarn v1也就是大多数人记忆里的yarn命令诞生于 2016 年当时主要针对 npm 的两大痛点安装太慢和依赖结构不可控。yarn v1 的做法是引入离线缓存和并行下载并且采用扁平的node_modules结构锁文件由yarn.lock精确锁定这让它在当时明显比 npm 快、比 npm 稳。但 yarn v1 本质上没有改变把包复制到 node_modules这个模型它的磁盘占用与 npm 是同一水平。真正的大变革是yarn v2Berry默认采用PlugnPlayPnP策略不再生成node_modules而是在.yarn/cache里存放压缩包通过.pnp.jsv2/v3 中是.pnp.cjs告诉 Node 运行时这个包应该去哪找、它依赖谁Node 解析模块时直接走这个索引文件跳过了文件系统的大量 stat 操作。yarn PnP 的速度理论上非常快但代价是兼容性牺牲。不是所有工具都支持 PnP 这种解析方式早期很多原生模块、构建工具直接翻车。团队里如果有人用了个不兼容 PnP 的脚本你会在装得很快和跑不起来之间反复横跳。所以目前实际项目中yarn 已经分裂成两派保守团队继续用 yarn v1激进团队用 yarn v2/v3 的 PnP 或 node-modules 模式。yarn v2 也提供了一个node-linker: node-modules选项能切回传统模式只是这样它的核心差异化优势就没了。1.4 为什么会出现首装慢、后续瞬间完成的现象很多人第一次用 pnpm 时最容易产生的疑问是第二次装为什么这么快 原因在上文已经说了——store 里已经有这份文件了pnpm install大部分时间不是在下载复制而是在算依赖、建链接。它只需要从 store 里把对应文件硬链接到项目目录这个操作是文件系统的元数据操作秒级完成。同样的逻辑也发生在多个项目复用同一个依赖的场景五个项目都用了 lodashnpm 会在每个项目里存一份pnpm 只会在 store 里存一份五个项目通过硬链接共享。你磁盘上存放的重复代码越少长期积累的空间收益越大。我自己的备用机上同时维护了十几个前端/Node 项目之前 npm 时期磁盘一度吃紧切到 pnpm 之后node_modules整体瘦身了 60% 以上。2. node_modules 目录结构与依赖解析机制三种哲学如果说安装速度是看得见的差异那node_modules的内部结构就是看不见但决定了你踩不踩坑的差异。这里藏着三个包管理器最本质的分歧。2.1 npm 和 yarn v1 的扁平化提升早年间 npmv2 时代的处理方式是严格嵌套每个包都装在自己的node_modules里依赖关系长什么样目录结构就是什么样。比如项目依赖 AA 依赖 BB 依赖 C那目录就是node_modules/A/node_modules/B/node_modules/C。这种方式最符合直觉但问题非常致命同一个包被不同层级的依赖引用时会拷贝出无数副本磁盘爆涨、路径过长Windows 上尤其明显安装速度也慢得离谱。npm v3 开始引入了扁平化提升hoisting能提到顶层的依赖全部提升到node_modules根目录。yarn v1 也效仿了这个策略。好处是路径短、共享方便坏处是依赖树可能并不唯一——如果 A 依赖 1.x 版本B 依赖 2.x 版本提升时发生冲突npm 会让其中一个版本待在顶层另一个版本挂在某个包下面造成项目里同时存在两个版本、但目录位置不确定的局面。2.2 幽灵依赖问题扁平结构埋下的雷扁平结构带来的最典型的坑就是幽灵依赖Phantom Dependency。简单说你的package.json里根本没声明某个包但代码里require(某个包)却能正常跑因为这个包被提升到了node_modules顶层。听起来像是捡了便宜实际上是个定时炸弹。举一个我遇到过的真实例子一个老项目里代码直接require(compression)但package.json里没有这个依赖。当初装express时它作为 express 的依赖被提升到了顶层所以一直能用。后来某次升级express 新版本调整了自身依赖不再依赖 compression 了它的位置从顶层消失业务代码瞬间报MODULE_NOT_FOUND。如果你用的是 pnpm这个坑从结构上就被堵死了。pnpm 那套符号链接结构的设计哲学就是每个包只能访问它声明过的依赖。项目根目录能访问的只有你自己安装的依赖看不到什么顺带上来的福利。2.3 pnpm 的符号链接设计结构严格但不完美pnpm 的node_modules结构大致是这样node_modules/.pnpm/是真实文件存放点命名规则是包名版本/node_modules/包名每个包都是独立的。node_modules根目录下你直接声明的依赖会被放一个符号链接指向.pnpm/包名版本/node_modules/包名。每个包的内部依赖B 依赖 C会通过node_modules/.pnpm/B版本/node_modules/里的符号链接链接到.pnpm/C版本/node_modules/C。这样做的结果是A 能拿到 BB 能拿到 C但 A 直接拿不到 C除非 A 自己声明了 C。依赖关系被严格约束在包声明的作用域内。这是 pnpm 最受人喜欢的一点彻底解决幽灵依赖也让依赖链路变得非常清晰。代价是什么一个是符号链接的兼容性问题。少数不按常理出牌的包比如某些 CLI 工具会直接去找一个自身没有声明、但历史上总能碰到的依赖在 pnpm 下确实会报错但这类情况随着 pnpm 普及已经越来越少。另一个是与某些打包工具/原生模块的冲突比如一些 node-gyp 编译流程会在硬链接的只读文件上做 patch写入失败时会非常费解。好在 pnpm 现在有node-linkerhoisted选项可以退回传统扁平结构真遇到不兼容时能应急。2.4 对你写代码有什么实际影响这里想单独强调一点目录结构不只是性能问题它还影响你调试代码的方式。用 npm/yarn v1你打开node_modules/lodash可以直接改源码验证想法改完就生效。用 pnpmnode_modules/lodash是个符号链接源文件在.pnpm目录里而且是硬链接/只读状态。你直接改动这个符号链接映射到的文件可能会影响所有引用它的项目。用 yarn PnP根本没有node_modules你想改源码都得先搞清楚.pnp索引怎么定位包。虽然有yarn unplug命令可以把某个包解压到可写目录但对习惯改第三方包源码来调试的开发者来说学习成本是真的不低。我的建议是如果你经常需要改 node_modules 里包的代码做本地调试npm 依然是最顺手的如果是团队协作的大项目pnpm 的严格结构带来的规范收益远远大于偶尔调试的便利损失。3. lockfile 与版本解析策略谁锁得住、谁锁得好搞包管理的人迟早会被为什么 CI 上装的和本地装的不一样这种问题折磨。三份锁文件的策略和定位各不相同。3.1 三份锁文件的差异锁文件格式解析目标特点package-lock.jsonJSON 嵌套树记录最终依赖树结构随依赖层级嵌套较大的项目文件会很庞大冲突合并是噩梦yarn.lock自定义文本格式记录解析策略Resolver 结果扁平条目按包名范围组织文件相对好读冲突处理也更容易pnpm-lock.yamlYAML记录 store 快照 importers结构清晰包含 peer 依赖信息文件大但 diff 相对可预测npm 的锁文件主要是保存安装结果把最终这颗依赖树原样记录下来下次安装照着这份快照还原。所以同是 package-lock.json两个项目的 diff 经常大得看不懂——因为只要你改了某个直接依赖的版本受影响的传递依赖们的提升位置可能重新洗牌diff 就会蔓延到一大片。yarn.lock 的思路是记录**如何解析出这个结果**按解析策略条目化存储。它的 diff 通常比 package-lock.json 更局部化这是 yarn 当年受青睐的原因之一。但它也保留了一些隐式行为同一个版本范围可能解析出多个不同的精确版本锁文件记录的是范围内选谁而不是所有包的唯一版本。pnpm-lock.yaml 是三份里信息量最足的它把每个项目的importers即你的直接依赖和每个包的snapshots已解析快照分开记录直接体现符号链接关系。这也意味着 pnpm 处理 peerDependencies 时能更诚实地表达不同 peer 组合会解析出不同实例这件事。3.2 peerDependency 冲突npm ERESOLVE 的日常挣扎使用 npm install 时你大概率见过这种报错npm warn ERESOLVE overriding peer dependency我自己最崩溃的一次是在一个用了 React 17 的老项目里装一个基于 React 18 写的新组件库。npm 会不厌其烦地提示 peer 依赖冲突有时会直接 EE 报错终止安装有时--legacy-peer-deps能绕过有时又不行。问题的本质是peerDependencies 表示我要求宿主环境必须有某个包、且版本要匹配但 npm v7 之后对 peer 冲突的处理变得非常激进一旦冲突严重就报错。pnpm 和 yarn 也会检查 peer 依赖但策略不一样尤其是 pnpm它倾向于把同一个包解析成多个实例来满足不同 peer 组合而不是简单粗暴地报错。具体到哪个好其实取决于你的心态。npm 的做法是发现冲突就大声喊出来用警告和错误逼你去处理pnpm 的做法是尽量让每个人都能好好活着同一个 lib 安装两份来匹配不同的 peer 要求但代价是 store 里多一份副本。在 peerDependency 复杂的生态比如 React 生态、一些插件系统里pnpm 的容错能力明显更省心。3.3 并发安装与可复现性还有一个细节经常被忽略三个工具对并发安装的处理不同。npm 在较老版本上是串行解压所以慢yarn v1 是并行解压但锁文件写入有竞态pnpm 由于 store 是全局共享的需要一套更复杂的并发锁机制来避免两个进程同时写 store 导致损坏。实际体验是pnpm 在多个项目同时安装时表现最稳定npm 在 CI 并发场景偶尔会出现package-lock 被覆盖的怪问题。如果你用 npm 做 CI 缓存建议把package-lock.json设为只读状态防止构建进程里去改动它。可复现性方面三者的 lockfile 理论上都能做到同样的 lock 文件 同样的 registry 同样的依赖树但pnpm 做得最彻底因为它的锁文件里连包的 peer 组合和符号链接布局都锁定了。npm 的 package-lock 在跨平台时偶尔会因 optionalDependencies比如某个包只在 Windows 上装、其他平台不装出现结构差异这在安装那些包含多平台二进制的包时特别明显——我装过某个 AI 编码工具的 CLI它的 Windows 版和 mac 版走的是不同的 optionalDependenciesnpm 装完后终端里全是missing optional dependency xxx/win32-x64这类警告看着吓人实际不影响运行。4. 命令行体验与迁移成本换工具不是改一行命令的事很多人以为换包管理器就是把命令缩写换成另一个真上手了才发现坑远比想象的多。我从 npm 切换到 pnpm 后花了大半天才把一些习惯和脚本调整到位。4.1 命令对照总览先给出一张日常高频操作对照表方便大家参考。操作npmyarn v1/v2pnpm安装所有依赖npm installyarnpnpm install新增某个依赖npm install pkgyarn add pkgpnpm add pkg新增开发依赖npm install -D pkgyarn add -D pkgpnpm add -D pkg全局安装npm i -g pkgyarn global add pkgpnpm add -g pkg移除依赖npm uninstall pkgyarn remove pkgpnpm remove pkg运行 package.json 脚本npm run devyarn devpnpm dev更新某个依赖npm update pkgyarn upgrade pkgpnpm up pkg查看依赖树npm lsyarn listpnpm list这里有个习惯差异要特别注意npm 的npm install pkg会直接写入 package.json因为它默认--save而 pnpm 也是写入的yarn 的yarn add更是写入。但如果你在 pnpm 下执行pnpm install xxx它的行为其实是在已安装的基础上再添加 xxx 并保存如果你本意只是安装所有依赖而误写了包名会得到意外结果。4.2 两个 npmnpm run 与 node_modules/.bin 的机制很多新手不懂的一点是npm install装完包之后为什么能直接在命令行里敲某个工具的命令比如npx vite、npm run build里用的vite是从哪来的实际上npm 在安装带bin字段的包时会在node_modules/.bin目录下生成符号链接Windows 上还会额外生成.cmd和.ps1包装脚本。当你执行npm run xxx时npm 会把node_modules/.bin注入到当前进程的 PATH 中于是在脚本里敲vite就等于执行了node_modules/.bin/vite。yarn 和 pnpm 也做了同样的事但有两个细节值得注意pnpm 的 .bin 链接是相对精确的它只链接你声明过的依赖对应的二进制不会被其他包的二进制污染。npm 因为扁平提升有时候node_modules/.bin里会混进来一堆你没直接声明过的工具命令。Windows 上 pnpm 和 npm 用 PowerShell 执行脚本时的差异——这就是很多人一运行就到npm.ps1报错的直接背景下面专门讲。4.3 从 npm 切到 pnpm我踩过的迁移坑分享几个真实的迁移体验希望能帮你少走弯路script 里的NODE_ENV赋值在 Linux/Mac 上build: NODE_ENVproduction webpack这种写法依赖 shell 语法。npm 交给 sh 执行pnpm 也交给 sh 执行这个基本没差别。但在 Windows 上npm 和 pnpm 对这类跨平台环境变量写法都有兼容问题建议用cross-env。postinstall 脚本npm 默认执行postinstall如果某个依赖里的 postinstall 要写文件比如 esbuild 的二进制下载pnpm 会把依赖文件设为只读可能会导致 postinstall 失败。遇到这种情况可以把这个包加入pnpm.neverBuiltDependencies或packageExtensions中单独处理。.npmrc的配置项registry、proxy这些在三个工具里基本通用但public-hoist-pattern、shamefully-hoist这类就是 pnpm 特有的。从 npx 迁移时之前配的npm config不会自动带过来需要重新pnpm config set。5. 高频报错与排查经验安装工具本身就有一堆坑只要你在社区里待过一段时间一定见过热搜里那一堆pm 无法加载、pnpm 不是内部或外部命令、npm 镜像源配置之类的问题。这节把这些高频坑集中讲一遍每个都是我从实际排查中总结出的完整思路。5.1 PowerShell 脚本禁止运行npm.ps1 报错的根因Windows 用户跑npm -v时经常在 PowerShell 里看到这样一串npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本。这个问题不在于 npm 本身而在于PowerShell 的执行策略Execution Policy。Node 官方安装包在 Windows 上生成的命令入口包括npm.cmd和npm.ps1。当你在 PowerShell 里输入npmShell 会优先找npm.ps1而系统默认执行策略Restricted禁止任何脚本运行于是报错。解决方案有三条按优先级排序用管理员权限打开 PowerShell执行Set-ExecutionPolicy -Scope CurrentUser RemoteSigned建议加-Scope CurrentUser只改当前用户。这样本机生成的脚本、可信来源的脚本可运行其他不受信任的脚本仍会被拦。不用 PowerShell而是改用cmd或Git Bash敲命令因为.cmd和 shell 脚本不受执行策略限制。很多老开发就是这么绕过去的。直接把命令写全npm.cmd -v但这样太别扭了不推荐日常使用。我自己的建议是第一种一次配置到位后续不管是 npm、pnpm 还是 yarn它们都会生成.ps1shim都不用再折腾。要注意的是pnpm 安装后如果遇到相同报错原因和解决方式完全相同。5.2 pnpm 不是内部或外部命令的排查链路这个报错和上面不太一样。系统提示pnpm : 无法将pnpm项识别为 cmdlet、函数、脚本文件或可运行程序的名称。意思是PATH 上找不到 pnpm 这个入口。常见原因按概率排根本没有全局安装 pnpm很多教程会带npm install -g pnpm但如果你 npm 的全局目录不在 PATH 里npm 装完 pnpm 后系统也找不到。先在终端里执行npm prefix -g看全局目录再确认 PATH 里有没有这个目录。曾经用过 Corepack 安装但 Corepack 被禁用Node 从 14 开始内置 Corepack可以零配置启用 pnpm/yarn。如果你在项目目录加了packageManager: pnpmx.y.z而 Corepack 因某种原因没启用就会提示找不到。检查corepack --version如果报错或显示Disabled需要手动启用。Corepack 与 npm 全局安装的 pnpm 冲突这种情况最难受——你在项目里跑pnpm -v却提示无法识别但系统里明明有一个 pnpm。原因可能是 Corepack 尝试加载的版本和当前 Node 版本不兼容或者缓存坏了。解决方式可以是corepack disable后卸载全局 pnpm 重装也可以反过来。排查这类问题的通用顺序是先问这个命令应该由谁提供npm 全局安装还是 corepack再检查 PATH 顺序最后检查具体安装目录下文件的执行权限/兼容性。别一上来就重装系统那太冤了。5.3 国内镜像源配置不只是改一行 registry如果你不在网络条件非常好的地区npm/pnpm 安装慢是常态。国内最主流的做法是配置镜像源。以下是我实测稳定的配置方案# npm 配置到国内镜像 npm config set registry https://registry.npmmirror.com # pnpm 配置注意 pnpm 有独立的配置作用域 pnpm config set registry https://registry.npmmirror.com还有一个容易被忽略的坑镜像源影响 lockfile 里的 resolved 字段。比如你平时用官方源装依赖package-lock.json 里的resolved是一长串https://registry.npmjs.org/...哪天为了提速改成镜像源重新 installnpm 会把 lockfile 里大量resolved全部替换成镜像域名diff 瞬间爆炸。pnpm 的 lockfile 里也有 registry 信息但格式更规整diff 出现这种问题时更容易排查。不及时把锁文件提交团队协作时就会出现我改一行锁文件动五千行的惨案。5.4 离线安装与缓存复用pnpm store 的另一面热搜里有pnpm 离线安装包、pnpm 下载失败这些词说明很多人在网络不稳定环境下挣扎。关于 pnpm 离线安装我先纠正一个常见误解pnpm 并不支持每次都完全离线安装至少首次安装时很多包仍需从远端拉取。它所谓的离线是指 store 里已有文件的场景下可以用pnpm install --offline强制不联网安装。前提是你之前已经完整装过一次。如果项目里新增了一个依赖但 store 里没有pnpm install --offline会直接报错。遇到这种情况正确的离线流程是在有网络的机器上pnpm install一次然后把store 目录和项目node_modules一并拷贝到离线机器上pnpm 会自动发现 store 并硬链接。npm 也有类似的离线能力npm install --prefer-offline、npm install --offline配合~/.npm/_cacache缓存目录使用。但 npm 的缓存组织不如 pnpm store 直观迁移到新机器时直接把整包缓存拷过去往往不太顺利。如果你有明确的离线场景需求pnpm 凭借内容寻址的 store 结构是目前最省心的一家。5.5 unsupported engine 报警npm 的碎碎念搜索词里还有这样一条npm warn ebadengine unsupported engine { package: sqlite... unsupported engine }很多人在npm install时见到这种警告就慌其实它绝大多数时候只是警告不阻断安装。含义是某个包的engines字段声明了它支持或排斥的 Node 版本而你当前的 Node 版本不在其支持范围内。npm 只会提示不会因此放弃安装。处理方式确认你项目实际运行时的 Node 版本与生产环境一致。如果应用能跑警告可以忽略如果确实需要可以通过.npmrc里的engine-stricttrue让 npm 把这类 warning 升级为 error主动暴露出问题。pnpm 也有engine-strict选项行为类似但它对 无效引擎范围 这类元数据更敏感选包时最好先看engines字段。6. Monorepo 与大型工程里的选择什么时候该换 pnpm最后一个大场景也是现在前端/Node 工程里讨论最多的地方Monorepo。团队有十几个子包互相依赖、共享代码用 npm 和用 pnpm 的体验完全是两个世界。6.1 workspace 原生支持对比先说结论三个工具都支持 workspace但支持的深度和默认行为不一样。npm 从 v7 开始原生支持workspaces可以在根目录的package.json里声明workspaces: [packages/*]然后用npm install统一安装和链接子包。但 npm 对 workspace 的依赖管理相对懒子包之间互相依赖时符号链接的处理偶尔会不到位。yarn v1 的 workspace 相当成熟很多早期 Monorepo 项目都靠它。yarn v2/v3 的 workspace 插件体系更强大但配置复杂度明显上升。pnpm 的 workspace 是目前我体验下来最顺的pnpm-workspace.yaml声明子包路径pnpm install会自动符号链接子包、处理共享依赖和锁文件还支持pnpm --filter按包筛选执行命令。它的 store 机制在 Monorepo 里优势被放大到极致——几十个子包共享同一个依赖版本时只占用一份磁盘空间这对每个子包都有 node_modules的老式 Monorepo 是降维打击。6.2 必须用 pnpm的真实原因很多开源项目标着安装必须用 pnpm比如搜索词里出现的 openmaic 相关的疑惑Why must use pnpm。这背后有几个真实原因严格依赖隔离开源库的 CI 需要同时验证依赖声明是否完整。npm 的扁平提升会掩盖漏声明但碰巧能用的问题导致库在不那么幸运的用户环境里直接炸。pnpm 的严格结构能从安装阶段就把这种隐患逼出来。store 的去重能力大型 monorepo 在 CI 上反复构建时pnpm 只需要下载一次依赖后续构建全走硬链接npm 每次在不同 checkout 目录里都要重新安装全量依赖CI 时间跟着飙。锁文件的可预测性pnpm-lock.yaml 对 peer 依赖冲突的处理更接近保留多个实例的方案这对插件染指很重的库比如各种构建插件、编译器工具链特别重要——每次装出来的依赖都能保持行为一致不容易被 npm 的 ERESOLVE 卡住。6.3 什么时候继续用 npm 也别有心理负担没必要把 pnpm 吹上天。我个人见过很多小项目、老项目、非专业前端团队维护的 Node 服务用 npm 一点问题没有。尤其是项目只有一个且依赖量很小pnpm 的 store 优势不显著反而多个符号链接结构会让心态上不熟悉的人困惑。团队里都是 Windows 老项目 对 node_modules 有执念的同事强行迁到 pnpm 可能引起一堆我明明 npm install 好好的的摩擦。此时收益未必扛得住阵痛。重度依赖某些不兼容 pnpm 的构建工具或本地 patch 流程如果你们已经在用 patch-package 或者在 node_modules 里改东西上了瘾npm 的扁平大杂烩反而更适合你们。工具没有银弹。依赖管理本质上是在解决一个问题如何让我电脑上的依赖和你电脑上的依赖尽量一致同时装得快、占得少。npm 兼容性最好yarn 是介于中间的保守改良方案pnpm 最有想象力但需要你花点时间去理解它的结构。我个人现在的选型习惯是新项目默认 pnpm团队需要协作且大家熟悉 npm 的用 npm遇到 monorepo 或者对磁盘空间敏感的机器则闭眼切 pnpm。最后分享一个实在的小建议——无论选哪个第一次转换之前先把项目里的 node_modules 和 lockfile 在 git 分支里隔离好转换完跑一遍 build、测试、lint 三件套确认无异常再合并别让迁移本身变成又一次事故。
返回列表