pnpm对npm及yarn降维打击详解


Posted in Javascript onAugust 05, 2022

正文

大家最近是不是经常听到 pnpm,我也一样。今天研究了一下它的机制,确实厉害,对 yarn 和 npm 可以说是降维打击。

那具体好在哪里呢? 我们一起来看一下。

我们按照包管理工具的发展历史,从 npm2 开始讲起:

npm2

用 node 版本管理工具把 node 版本降到 4,那 npm 版本就是 2.x 了。

pnpm对npm及yarn降维打击详解

然后找个目录,执行下 npm init -y,快速创建个 package.json。

然后执行 npm install express,那么 express 包和它的依赖都会被下载下来:

pnpm对npm及yarn降维打击详解

展开 express,它也有 node_modules:

pnpm对npm及yarn降维打击详解

再展开几层,每个依赖都有自己的 node_modules:

pnpm对npm及yarn降维打击详解

也就是说 npm2 的 node_modules 是嵌套的。

这很正常呀?有什么不对么?

这样其实是有问题的,多个包之间难免会有公共的依赖,这样嵌套的话,同样的依赖会复制很多次,会占据比较大的磁盘空间。

这个还不是最大的问题,致命问题是 windows 的文件路径最长是 260 多个字符,这样嵌套是会超过 windows 路径的长度限制的。

当时 npm 还没解决,社区就出来新的解决方案了,就是 yarn:

yarn

yarn 是怎么解决依赖重复很多次,嵌套路径过长的问题的呢?

铺平。所有的依赖不再一层层嵌套了,而是全部在同一层,这样也就没有依赖重复多次的问题了,也就没有路径过长的问题了。

我们把 node_modules 删了,用 yarn 再重新安装下,执行 yarn add express:

这时候 node_modules 就是这样了:

pnpm对npm及yarn降维打击详解

全部铺平在了一层,展开下面的包大部分是没有二层 node_modules 的:

pnpm对npm及yarn降维打击详解

当然也有的包还是有 node_modules 的,比如这样:

pnpm对npm及yarn降维打击详解

为什么还有嵌套呢?

因为一个包是可能有多个版本的,提升只能提升一个,所以后面再遇到相同包的不同版本,依然还是用嵌套的方式。

npm 后来升级到 3 之后,也是采用这种铺平的方案了,和 yarn 很类似:

pnpm对npm及yarn降维打击详解

当然,yarn 还实现了 yarn.lock 来锁定依赖版本的功能,不过这个 npm 也实现了。

yarn 和 npm 都采用了铺平的方案,这种方案就没有问题了么?

并不是,扁平化的方案也有相应的问题。

最主要的一个问题是幽灵依赖,也就是你明明没有声明在 dependencies 里的依赖,但在代码里却可以 require 进来。

这个也很容易理解,因为都铺平了嘛,那依赖的依赖也是可以找到的。

但是这样是有隐患的,因为没有显式依赖,万一有一天别的包不依赖这个包了,那你的代码也就不能跑了,因为你依赖这个包,但是现在不会被安装了。

这就是幽灵依赖的问题。

而且还有一个问题,就是上面提到的依赖包有多个版本的时候,只会提升一个,那其余版本的包不还是复制了很多次么,依然有浪费磁盘空间的问题。

那社区有没有解决这俩问题的思路呢?

当然有,这不是 pnpm 就出来了嘛。

那 pnpm 是怎么解决这俩问题的呢?

pnpm

回想下 npm3 和 yarn 为什么要做 node_modules 扁平化?不就是因为同样的依赖会复制多次,并且路径过长在 windows 下有问题么?

那如果不复制呢,比如通过 link。

首先介绍下 link,也就是软硬连接,这是操作系统提供的机制,硬连接就是同一个文件的不同引用,而软链接是新建一个文件,文件内容指向另一个路径。当然,这俩链接使用起来是差不多的。

如果不复制文件,只在全局仓库保存一份 npm 包的内容,其余的地方都 link 过去呢?

这样不会有复制多次的磁盘空间浪费,而且也不会有路径过长的问题。因为路径过长的限制本质上是不能有太深的目录层级,现在都是各个位置的目录的 link,并不是同一个目录,所以也不会有长度限制。

没错,pnpm 就是通过这种思路来实现的。

再把 node_modules 删掉,然后用 pnpm 重新装一遍,执行 pnpm install。

你会发现它打印了这样一句话:

pnpm对npm及yarn降维打击详解

包是从全局 store 硬连接到虚拟 store 的,这里的虚拟 store 就是 node_modules/.pnpm。

我们打开 node_modules 看一下:

pnpm对npm及yarn降维打击详解

确实不是扁平化的了,依赖了 express,那 node_modules 下就只有 express,没有幽灵依赖。

展开 .pnpm 看一下:

pnpm对npm及yarn降维打击详解

所有的依赖都在这里铺平了,都是从全局 store 硬连接过来的,然后包和包之间的依赖关系是通过软链接组织的。

比如 .pnpm 下的 expresss,这些都是软链接,

pnpm对npm及yarn降维打击详解

也就是说,所有的依赖都是从全局 store 硬连接到了 node_modules/.pnpm 下,然后之间通过软链接来相互依赖。

官方给了一张原理图,配合着看一下就明白了:

pnpm对npm及yarn降维打击详解

这就是 pnpm 的实现原理。

那么回过头来看一下,pnpm 为什么优秀呢?

首先,最大的优点是节省磁盘空间呀,一个包全局只保存一份,剩下的都是软硬连接,这得节省多少磁盘空间呀。

其次就是快,因为通过链接的方式而不是复制,自然会快。

这也是它所标榜的优点:

pnpm对npm及yarn降维打击详解

相比 npm2 的优点就是不会进行同样依赖的多次复制。

相比 yarn 和 npm3+ 呢,那就是没有幽灵依赖,也不会有没有被提升的依赖依然复制多份的问题。

这就已经足够优秀了,对 yarn 和 npm 可以说是降维打击。

总结

pnpm 最近经常会听到,可以说是爆火。本文我们梳理了下它爆火的原因:

npm2 是通过嵌套的方式管理 node_modules 的,会有同样的依赖复制多次的问题。

npm3+ 和 yarn 是通过铺平的扁平化的方式来管理 node_modules,解决了嵌套方式的部分问题,但是引入了幽灵依赖的问题,并且同名的包只会提升一个版本的,其余的版本依然会复制多次。

pnpm 则是用了另一种方式,不再是复制了,而是都从全局 store 硬连接到 node_modules/.pnpm,然后之间通过软链接来组织依赖关系。

这样不但节省磁盘空间,也没有幽灵依赖问题,安装速度还快,从机制上来说完胜 npm 和 yarn。

pnpm 就是凭借这个对 npm 和 yarn 降维打击的,更多关于pnpm降维打击npm yarn的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
js 单引号 传递方法
Jun 22 Javascript
制作jquery遮罩层效果导航菜单代码分享
Dec 25 Javascript
jQuery实现的图文高亮滚动切换特效实例
Aug 10 Javascript
浅谈$_FILES数组为空的原因
Feb 16 Javascript
Node.js读取文件内容示例
Mar 07 Javascript
AngularJS 中的数据源的循环输出
Oct 12 Javascript
AngularJS实现的base64编码与解码功能示例
May 17 Javascript
使用JavaScript生成罗马字符的实例代码
Jun 08 Javascript
Vue-cli配置打包文件本地使用的教程图解
Aug 02 Javascript
详解VUE项目中安装和使用vant组件
Apr 28 Javascript
用Vue编写抽象组件的方法
May 06 Javascript
使用Layer组件弹出多个对话框(非嵌套)与关闭及刷新的例子
Sep 25 Javascript
JS前端可视化canvas动画原理及其推导实现
Aug 05 #Javascript
JS前端使用canvas实现扩展物体类和事件派发
Aug 05 #Javascript
JS前端canvas交互实现拖拽旋转及缩放示例
Aug 05 #Javascript
canvas 中如何实现物体的框选
Aug 05 #Javascript
JS前端使用canvas实现物体的点选示例
Aug 05 #Javascript
前端canvas中物体边框和控制点的实现示例
Aug 05 #Javascript
JS前端轻量fabric.js系列物体基类
Aug 05 #Javascript
You might like
Google Voice 短信发送接口PHP开源版(2010.5更新)
2010/07/22 PHP
PHP使用正则表达式清除超链接文本
2013/11/12 PHP
使用PHP备份MySQL和网站发送到邮箱实例代码
2013/11/28 PHP
php教程之魔术方法的使用示例(php魔术函数)
2014/02/12 PHP
php中debug_backtrace、debug_print_backtrace和匿名函数用法实例
2014/12/01 PHP
PHP多线程模拟实现秒杀抢单
2018/02/07 PHP
清除网页历史记录,屏蔽后退按钮!
2008/12/22 Javascript
JS 密码强度验证(兼容IE,火狐,谷歌)
2010/03/15 Javascript
基于jquery的一个图片hover的插件
2010/04/24 Javascript
jquery操作下拉列表、文本框、复选框、单选框集合(收藏)
2014/01/08 Javascript
js模拟hashtable的简单实例
2014/03/06 Javascript
JS实现动态给图片添加边框的方法
2015/04/01 Javascript
Angular5中调用第三方库及jQuery的添加的方法
2018/06/07 jQuery
从vue源码看props的用法
2019/01/09 Javascript
jQuery选择器之基本选择器用法实例分析
2019/02/19 jQuery
[10:18]2018DOTA2国际邀请赛寻真——找回自信的TNCPredator
2018/08/13 DOTA
web.py获取上传文件名的正确方法
2014/08/26 Python
Python中的引用和拷贝浅析
2014/11/22 Python
python实现五子棋游戏
2019/06/18 Python
Python中那些 Pythonic的写法详解
2019/07/02 Python
对python中基于tcp协议的通信(数据传输)实例讲解
2019/07/22 Python
Django使用unittest模块进行单元测试过程解析
2019/08/02 Python
Django框架 信号调度原理解析
2019/09/04 Python
Python3 Tensorlfow:增加或者减小矩阵维度的实现
2020/05/22 Python
python 匿名函数与三元运算学习笔记
2020/10/23 Python
2014年大学生自我评价
2014/01/19 职场文书
双十佳事迹材料
2014/01/29 职场文书
国际贸易系求职信
2014/08/09 职场文书
国家领导干部党的群众路线教育实践活动批评与自我批评材料
2014/09/23 职场文书
2015年实习单位评语
2015/03/25 职场文书
运输公司工作总结
2015/08/11 职场文书
2016年小学“感恩教师”主题队日活动总结
2016/04/01 职场文书
使用css样式设计一个简单的html登陆界面的实现
2021/03/30 HTML / CSS
用Python提取PDF表格的方法
2021/04/11 Python
pandas DataFrame.shift()函数的具体使用
2021/05/24 Python
windows11选中自动复制怎么开启? Win11自动复制所选内容的方法
2022/07/23 数码科技