概览
📝 PnP 是 Yarn 的一项功能,请勿将其与 npm 混淆,后者是一个独立的 JavaScript 包管理器。
即插即用是一种替代的安装策略,已于 2018 年 9 月推出。它具有许多有趣特性,适用于大量项目,并设计为与当前生态系统兼容。
常规安装的执行方式很简单:Yarn 生成一个 node_modules
目录,然后 Node 可以使用该目录。在此上下文中,Node 对包一无所知:它只考虑文件。“此文件是否存在于此处?否?那么看看父级 node_modules
。是否存在于此处?仍未找到?太糟糕了...查看父文件夹!”- 它会一直执行此操作,直到匹配某个可能性。效率十分低下。
仔细思考一下,Yarn 了解依赖项树的所有内容,甚至可以安装它!那么,为什么需要让 Node 在磁盘上查找包?我们为什么不直接查询 Yarn 并让它告诉我们到哪里查找包 Y 所需的包 X?这就是即插即用 (简称 PnP)。我们不再生成 node_modules
目录并留给 Node 解析,而是生成一个 .pnp.js
文件并让 Yarn 告诉我们到哪里查找我们的包。这样做有很多好处
-
node_modules
目录包含大量文件。生成它占运行yarn install
(热缓存)所需时间的 70% 以上。由于这个过程受 I/O 限制,包管理器无法真正优化它 - 我们可以使用硬链接或写时复制,但是即使这样,我们仍然需要执行大量系统调用,这会大幅降低速度。 -
由于 Node 没有“包”的概念,因此它不知道某个文件是否可用,更不知道是否应访问它。完全可能你编写的代码在开发中可以正常运行,但在生产中出现故障,因为你忘了在
package.json
中列出某个依赖项,而且在你遇到问题并浪费一天调查该问题之前,你不会意识到这一点。 -
即使在运行时,Node 解析也需要进行大量
stat
和readdir
调用,以确定解析应该在何处结束。这极大地浪费了时间,也是 Node 应用启动如此耗时的一部分原因,因为在开始执行之前,Node 必须花费时间查询 Yarn 已可以提供的信息的文件系统。 -
最后,
node_modules
文件夹的设计非常不实用,因为它无法像人们希望的那样有效地取消包重复。由于同名但版本不同的两个包无法共存于同一个目录,我们无法保证完美提升。同样,由于node_modules
深度嵌套的方式依赖于项目依赖,它们无法在一个项目与另一个项目之间共享。
所有这些问题以及更多问题都由 Plug’n’Play 解决。