## 关于 Yarn 即插即用你可能需要知道这些最近在项目里又用到了 Yarn特别是它的 Plug’n’Play 模式也就是大家常说的 PnP。这东西刚出来的时候争议不小有人觉得是革命性的改进也有人觉得是徒增烦恼。用了一段时间之后觉得有些想法可以聊聊不一定全面但都是实际踩过坑之后的体会。它到底是什么简单来说Yarn PnP 想解决的是 node_modules 这个老问题。传统的 node_modules 是个什么情况呢每个项目都要把依赖包完整地下载到本地形成一个巨大的文件夹。项目多了硬盘上就堆满了重复的文件。这还不算因为 Node.js 的模块解析机制经常会出现依赖嵌套太深甚至路径过长导致 Windows 系统报错的情况。PnP 换了个思路。它不再生成那个庞大的 node_modules 文件夹而是创建一个叫做.pnp.cjs的文件新版本是.pnp.js。这个文件本质上是一张“地图”精确记录了每个依赖包在磁盘上的实际位置以及它们之间的依赖关系。你可以把它想象成一本图书馆的索引目录而不是把整座图书馆的书都搬到你家里。这样做最直接的好处是项目的依赖不再分散在成千上万个文件夹里而是集中存放在一个统一的缓存目录中比如全局的 yarn 缓存。同一个版本的包在整个电脑上只存一份。项目本身变得非常“干净”没有了 node_modules安装速度也快了很多因为很多情况下只是从缓存里链接过来而不是重新下载和解压。它能做什么首先最明显的是节省磁盘空间和安装时间。对于需要同时维护多个前端项目的人来说这个提升是能切身感受到的。以前新拉一个项目npm install或yarn之后去喝杯咖啡是常事现在可能几十秒就好了。其次它带来了确定性和安全性。传统的 node_modules 结构下依赖的查找是有“向上递归”机制的这有时会导致一个项目意外地使用了父目录下的某个包版本从而引发难以调试的问题。PnP 通过那张精确的“地图”完全杜绝了这种不确定性。一个包该用哪个版本在.pnp.cjs文件里写得清清楚楚没有任何歧义。这也意味着只要这个文件对了在任何机器上、任何目录下项目的依赖行为都是一致的。另外它还能帮助发现一些隐藏的依赖问题。比如你的代码里如果偷偷用了某个包但没有在 package.json 里声明在传统模式下它可能因为其他包间接依赖了它而“侥幸”能运行。但在 PnP 模式下这张“地图”里没有记录你的代码在解析这个包时就会立刻失败这就逼着开发者去显式地声明所有依赖。怎么开始用使用起来并不复杂。如果你的 Yarn 版本是 2 或更高也就是 Berry 版本那么 PnP 默认就是启用的。你可以通过运行yarn --version来确认。如果是经典的 1.x 版本需要先升级到新版本。初始化一个新项目很简单yarn init -2就可以了。对于一个现有的项目迁移稍微有点步骤但官方文档写得比较清楚。大体上是在项目根目录下创建一个.yarnrc.yml文件设置nodeLinker: pnp然后删掉现有的 node_modules如果存在再运行yarn install。Yarn 就会为你生成.pnp.cjs文件和.yarn/cache文件夹存放压缩包。迁移后可能会遇到一些问题主要是某些工具或脚本假设了 node_modules 的存在。这时候需要看看错误信息通常需要为这些工具添加对应的支持。Yarn 提供了yarn dlx命令来直接运行临时的包也提供了 SDK 来集成 IDE 的支持。一些实践中的心得刚开始用 PnP最大的不习惯可能就是某些工具“失灵”了。比如一些旧的脚手架工具、或者测试框架的配置它们可能会直接去 node_modules 里找东西。这时候不能硬来得去查这些工具是否已经支持 PnP或者有没有对应的插件。社区对主流工具如 Webpack、Jest、ESLint的支持现在已经比较好了通常都有详细的配置说明。对于团队项目建议把.yarn/cache文件夹也提交到代码仓库里。这听起来有点反直觉但它能保证所有团队成员、以及 CI/CD 环境拥有完全一致的依赖文件真正实现“零安装”。这个缓存目录里存放的是压缩包体积比解压后的 node_modules 小很多而且 Git 对大文件的处理也有优化。当然这需要团队对 Git 的.gitignore规则做一些调整。另一个小细节是因为依赖解析机制变了以前一些基于require.resolve来查找模块路径的 hacky 代码可能会失效。在写工具脚本或者配置时最好使用 Yarn 提供的 API 或者更标准的方式来处理模块路径。和传统方式的对比和传统的 node_modules 方式比PnP 更像一个“管理者”而前者更像一个“搬运工”。传统方式简单粗暴把东西都堆到你面前怎么用你自己看着办。PnP 则建立了一套索引体系你需要通过它来获取依赖。和 npm 或 pnpm 相比各有侧重。npm 目前还是以传统 node_modules 为主虽然也有改进计划。pnpm 同样致力于解决依赖重复和磁盘空间问题它采用了“硬链接”的方式在全局 store 里存一份然后在项目的 node_modules 里创建硬链接效果上和 PnP 节省空间的目标类似但依然保留了 node_modules 的目录结构所以对现有生态工具的兼容性通常更好一些。选择哪一个往往不是纯粹的技术优劣问题更多是看团队的技术栈、对生态工具链的依赖程度以及是否愿意为了新的工作流去适配和调整。如果是一个全新的、技术栈比较现代的项目尝试 PnP 会是一个很顺畅的体验。如果是一个庞大的、依赖了大量老旧工具和脚本的遗留项目迁移可能会遇到不少阻力。总的来说Yarn PnP 是一个大胆且思路清晰的尝试它直指了 Node.js 生态中依赖管理的一些根本性问题。虽然它要求开发者和工具链做出一些改变但带来的确定性、速度和空间节省对于很多项目来说是值得的。技术选型从来不是寻找一个完美的银弹而是为特定的场景寻找最合适的工具。