Yarn 团队在春节前公布了 Yarn 2.0 的规划。其中提到了一个以前没据说过的名词 “PnP”。发现 Yarn 的这个功能早在 18 年 9 月份就被提出并实现了。因而花了一些时间了解了一下它的工做原理以及解决的问题并整理除了本篇文章。html
Yarn 团队开发 PnP 特性最直接的缘由就是现有的依赖管理方式效率过低。引用依赖时慢,安装依赖时也慢。node
先说说 Node 在处理依赖引用时的逻辑,这个流程会有以下两种状况:react
require()
调用的参数是一个核心模块(例如 "fs"、"path"等)或者是一个本地相对路径(例如 ./module-a.js
或 /my-li/module-b.js
),那么 Node 会直接使用对应的文件。node_modules
的目录:
node_modules
,若是没有则到父目录查找,以此类推直到系统根目录。node_modules
目录以后,再在该目录中寻找名为 moduleName.js
的文件或是名为 moduleName
的子目录。此处旨在说明问题,对 Node 内部模块解析逻辑作了简化描述git
可见 Node 在解析依赖时须要进行大量的文件 I/O 操做,效率并不高。github
再来看看安装依赖时发生了什么,现阶段 yarn install
操做会执行如下 4 个步骤:npm
node_modules
目录其中第 4 步一样涉及大量的文件 I/O,致使安装依赖时效率不高(尤为是在 CI 环境,每次都须要安装所有依赖)。json
Facebook 的工程师受够了这些问题决定寻找一个能完全解决问题同时还能够与现有生态兼容的解决方案。这即是 Plug'n'Play 特性,简称 PnP。它已在 Facebook 内部测试了一段时间,如今 Yarn 团队决定与社区分享并共同优化该方案。api
PnP 的具体工做原理是,做为把依赖从缓存拷贝到 node_modules
的替代方案,Yarn 会维护一张静态映射表,该表中包含了如下信息:缓存
这个映射表在 Yarn 的 PnP 实现中对应项目目录中的 .pnp.js
文件。bash
这个 .pnp.js
文件是如何生成,Yarn 又是如何利用它的呢?
在安装依赖时,在第 3 步完成以后,Yarn 并不会拷贝依赖到 node_modules
目录,而是会在 .pnp.js
中记录下该依赖在缓存中的具体位置。这样就避免了大量的 I/O 操做同时项目目录也不会有 node_modules
目录生成。
同时 .pnp.js
还包含了一个特殊的 resolver,Yarn 会利用这个特殊的 resolver 来处理 require()
请求,该 resolver 会根据 .pnp.js
文件中包含的静态映射表直接肯定依赖在文件系统中的具体位置,从而避免了现有实如今处理依赖引用时的 I/O 操做。
从 PnP 的实现方案能够看出,同一个系统上不一样项目引用的相同依赖的相同版本实际都是指向的缓存中的同一个目录。这带来了几个最直观的好处:
首先你须要 Yarn 1.12+ 版本。而后根据你的具体场景能够选择:
create-react-app
建立项目时开启 PnPcreate-react-app
已经集成了对 PnP 的支持。只需在建立项目时添加 --use-pnp
参数便可。
npx create-react-app testapp --use-pnp
复制代码
只需在项目中执行:
yarn --pnp
复制代码
便可开启 PnP 特性。
pkg.installConfig
字段在项目中开启 PnP 特性后,Yarn 会在 package.json
文件中建立一个 installConfig
字段:
{
"installConfig": {
"pnp": true
}
}
复制代码
只要 installConfig.pnp
的值是一个真值且当前版本的 Yarn 支持,PnP 特性就会被启用。
npm script
或是运行 .js
文件因为在开启了 PnP 的项目中再也不有 node_modules
目录,全部的依赖引用都必须由 .pnp.js
中的 resolver 处理。所以不管是执行 script 仍是用 node
直接执行一个 JS 文件,都必须经由 Yarn 处理。必须经过 yarn run
或是 yarn node
执行。
在开发过程当中咱们有时会直接修改 node_modules
目录下的依赖来调试。但在 PnP 模式下,因为依赖都指向了全局缓存,咱们再也不能够直接修改这些依赖。
针对这种场景,Yarn 提供了 yarn unplug packageName
来将某个指定依赖拷贝到项目中的 .pnp/unplugged
目录下。以后 .pnp.js
中的 resolver 就会自动加载这个 unplug 的版本。
调试完毕后,再执行 yarn unplug --clear packageName
可移除本地 .pnp/unplugged
中的对应依赖。
目前 PnP 仍是一个相对比较新的特性,你们能够尝试在本地开发环境中启用 PnP 来感觉一下它带来的全新体验。遇到问题能够及时反馈到 Yarn 的 issue 列表中。
参考连接:
更多文章,请关注咱们团队的公众号:全栈探索