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 相关文章推荐
兼容最新firefox、chrome和IE的javascript图片预览实现代码
Aug 08 Javascript
ajax跨域调用webservice的实现代码
May 09 Javascript
JS实现星星评分功能实例代码(两种方法)
Jun 09 Javascript
jquery实现文字单行横移或翻转(上下、左右跳转)
Jan 08 Javascript
jQuery回调方法使用示例
Jun 26 jQuery
vue2.0的contextmenu右键弹出菜单的实例代码
Jul 24 Javascript
angularjs实现天气预报功能
Jun 16 Javascript
JS获取数组中出现次数最多及第二多元素的方法
Oct 27 Javascript
Vue shopCart 组件开发详解
Jan 26 Javascript
vue组件之间数据传递的方法实例分析
Feb 12 Javascript
createObjectURL方法实现本地图片预览
Sep 30 Javascript
原生js实现html手机端城市列表索引选择城市
Jun 24 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
用PHP实现Ftp用户的在线管理的代码
2007/03/06 PHP
一篇有意思的技术文章php介绍篇
2010/10/26 PHP
php实现获取局域网所有用户的电脑IP和主机名、及mac地址完整实例
2014/07/18 PHP
微信公众平台开发教程②微信端分享功能图文详解
2019/04/10 PHP
yii2.0框架使用 beforeAction 防非法登陆的方法分析
2019/09/11 PHP
Laravel5.5 数据库迁移:创建表与修改表示例
2019/10/23 PHP
扩展easyui.datagrid,添加数据loading遮罩效果代码
2010/11/02 Javascript
读取input:file的路径并显示本地图片的方法
2013/09/23 Javascript
用js的document.write输出的广告无阻塞加载的方法
2014/06/05 Javascript
Javascript核心读书有感之表达式和运算符
2015/02/11 Javascript
js字符串操作总结(必看篇)
2016/11/22 Javascript
Node.js设置CORS跨域请求中多域名白名单的方法
2017/03/28 Javascript
react-router JS 控制路由跳转实例
2017/06/15 Javascript
JS鼠标滚动分页效果示例
2017/07/05 Javascript
js序列化和反序列化的使用讲解
2019/01/19 Javascript
Vue从TodoList中学父子组件通信
2019/02/05 Javascript
JS中的算法与数据结构之字典(Dictionary)实例详解
2019/08/20 Javascript
Javascript实现简易天数计算器
2020/05/18 Javascript
jQuery编写QQ简易聊天框
2020/08/27 jQuery
基于vuex实现购物车功能
2021/01/10 Vue.js
Python中如何使用if语句处理列表实例代码
2019/02/24 Python
python实现nao机器人身体躯干和腿部动作操作
2019/04/29 Python
python 的 scapy库,实现网卡收发包的例子
2019/07/23 Python
python实现静态服务器
2019/09/05 Python
Anaconda之conda常用命令介绍(安装、更新、删除)
2019/10/06 Python
Python @property装饰器原理解析
2020/01/22 Python
python脚本和网页有何区别
2020/07/02 Python
python 使用建议与技巧分享(四)
2020/08/18 Python
Myprotein比利时官方网站:欧洲第一运动营养品牌
2020/10/04 全球购物
大学生自我鉴定评语
2014/01/27 职场文书
爱牙日活动总结
2014/08/29 职场文书
会议通知
2015/04/15 职场文书
2015暑假假期总结
2015/07/13 职场文书
标枪加油稿
2015/07/22 职场文书
Nginx优化服务之网页压缩的实现方法
2021/03/31 Servers
jQuery ajax - getScript() 方法和getJSON方法
2021/05/14 jQuery