引
随着 JS 生态的持续发展,NPM 上的包数量已经突破 100W 大关。当前整个前端应用生态已经离不开这些功能各异的 NPM 包了。现在,我们仅仅想运行一个带框架的 HelloWorld Demo,整个的磁盘占用量都在几百M上下,甚至有些会超过 1G(主要是来自 NPM 包),再加上包的下载时间,想想还是有点可怕~
NPM 包依赖的问题,在之前有个有个很严重的问题是层级依赖(嵌套依赖,加上版本兼容性规则叠加的复杂性),在 npm 4.x(大概是 4.x) 的时候通过扁平化 node_modules
进行了缓解(现在不怎么痛了),那么现如今整个依赖臃肿的问题,有哪些方式可以去解呢?
思
分析 NPM 包依赖太大的问题,核心痛点是:
- 依赖太大,导致磁盘占用高(这个还算好,毕竟磁盘不那么值钱)
- 依赖太大,初始化时网络开销大,耗时长(时间可值钱了)
从单个仓库视角来看,依赖臃肿,那么很直白的方式就是进行瘦身。从这点思考,比较容易想到如下:
- NPM 包优化,保障发布到 NPM 仓库的都是必须内容 - 减小包自身体积
- 仓库内依赖包控制,严格控制对依赖包的引用,避免不必要的包依赖 - 精细化控制依赖
从以上两点来看,第一点可以从根源上优化包的体积,所有的依赖方都会收益,但这依赖来个各个地方的开发者去协力做这个事情,难度可见一斑,从实现层面,基本不可行。第二点的话,需要在构建项目的时候,去思考的严控包依赖,会极大的增加研发人员的负担,也难以执行(ROI 不高)。
BTW:从单仓库来说,为了缓解网络开销,还可以直接把 node_modules 目录推送到仓库,更进一步,压缩之后推送到仓库。当然,同样治标不治本,也会增加其他方面的复杂度。
从整个机器视角来看(我们一般会在一台机器上运行 N 个依赖,其中又会存在大量重复的依赖),我们是否可以考虑把依赖项进行中心化,然后我们在运行项目的时候再进行 resolve?
其实这个思路在很多后端语言的包管理上已经用上了,如 Nuget(.Net 的包管理)。在缓解 NPM 的依赖臃肿和下载慢的问题上,多年间也有一些探索:
- yarn 的 依赖缓存
- npm 6.x(应该是吧)依赖缓存(被 yarn 倒逼)
- cnpm 的软链接
这些方案在实际运用层面,都这两个痛点有所缓解,但还不够彻底。接下来来看一种比较彻底的解决办法
yarn PnP
方案易想,但只有落地才能产生价值。PnP 方案思路新么?一点也不,核心来看就是对后端常见的依赖管理机制的复刻,甚至我在几年前也都有想到过这个解决办法(but,不落地都是空谈)
PnP 是什么?
想了解更多,可以查阅:https://classic.yarnpkg.com/en/docs/pnp/
全称 Plug'n'P
,直译就是“即插即用”(BTW:这个直译和实际功能我觉得是有点偏差的,暂且不管直译,先叫 PnP 吧)。核心思路就是在每台机器上,单独创建一个包目录,来管理所有被依赖的 NPM 包,既然是中心化的管理思路,那么一台机器上的包就不再需要重复安装了,项目运行的时候,先对依赖包从这个中心化目录进行 resolve,然后再进行启动:
此处缺一张图
怎么用?
想必看到这个特性,势必会想着赶紧用起来,那么接下来就看看怎么来使用这个:
- 首先,我们得先安装
yarn
,执行npm i -g yarn
即可(至于 node 和 npm 我想不用多说) - 在项目目录下执行
yarn --pnp
来启用 PnP,想了解更多命令,可以执行yarn -h
查看 在
pacakge.json
中确保如下配置项存在,则可认为启用了 PnP// package.json
{
...
"installConfig": {
"pnp": true
}
}
注意:
当前 PnP 特性还不支持 Windows,截至目前(2020年9月5日),在 Windows 上执行,会出现
warning Plug'n'Play on Windows doesn't support the cache and project to be kept on separate drives
异常- PnP 依赖
yarn
支持,所以需要 Yarn 得版本至少1.12+
运行原理
待补充