pnpm比npm、yarn好在哪里?

前言

pnpm对比npm/yarn的优点:

  • 更快速的依赖下载
  • 更高效的利用磁盘空间
  • 更优秀的依赖管理
    我们按照包管理工具的发展历史,从 npm2 开始讲起:

npm2

使用早期的npm1/2安装依赖,node_modules文件会以递归的形式呈现,严格按照package.json结构以及次级依赖的package.json将依赖安装到各自的node_modules中,直至次级依赖不再依赖其他模块

举例:

  1. foo -> bar,foo依赖于bar
node_modules
└─ foo├─ index.js├─ package.json└─ node_modules└─ bar├─ index.js└─ package.json
  1. foo1 -> bar,foo2 -> bar,bar会被安装两次
node_modules
├─ foo1
│  ├─ index.js
│  ├─ package.json
│  └─ node_modules
│      └─ bar
│          ├─ index.js
│          └─ package.json
└─ foo2├─ index.js├─ package.json└─ node_modules└─ bar├─ index.js└─ package.json
  1. 一些其他问题
    ● 依赖层级太深,会导致文件路径过长(windows 的文件路径最长是 260 多个字符,这样嵌套是会超过 windows 路径的长度限制的。)
    ● 这样的嵌套,重复的包被安装,导致node_modules文件体积巨大,占用过多的磁盘空间

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

npm3/yarn

yarn 是怎么解决依赖重复很多次,嵌套路径过长的问题的呢?
铺平。所有的依赖不再一层层嵌套了,而是全部在同一层,采用“扁平化”的方式去管理依赖,这样也就没有依赖重复多次的问题了,也就没有路径过长的问题了。

举例:

  1. foo1 -> bar,foo2 -> bar
node_modules
├─ bar
│  ├─ index.js
│  └─ package.json
├─ foo1
│  ├─ index.js
│  └─ package.json
└─ foo2├─ index.js└─ package.json
  1. foo1 -> bar@1.0.0,foo2 -> bar@2.0.0
node_modules
├─ bar@1.0.0
│  ├─ index.js
│  └─ package.json
├─ foo1
│  ├─ index.js
│  └─ package.json
└─ foo2├─ index.js└─ package.json└─ node_modules└─ bar@2.0.0├─ index.js└─ package.json

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

使用扁平化的方案,解决了npm2中出现的问题,但是也带来一些问题:

  1. 幽灵依赖
    就是明明没有在dependencies中声明的依赖,但是却可以require进来。很容易理解,就是依赖都铺平了,那依赖的依赖也是可以找到的。

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

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

那社区有没有解决这俩问题的思路呢?
当然有,这不是 pnpm 就出来了嘛。
那 pnpm 是怎么解决这俩问题的呢?

pnpm

回想下 npm3 和 yarn 为什么要做 node_modules 扁平化?不就是因为同样的依赖会复制多次,并且路径过长在 windows 下有问题么?
那如果不复制呢,比如通过 link。
首先介绍下 link,也就是软硬连接,这是操作系统提供的机制。

  1. 硬连接就是同一个文件的不同引用,
  2. 而软链接是新建一个文件,文件内容指向另一个路径。
  3. 当然,这俩链接使用起来是差不多的。

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

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

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

再把 node_modules 删掉,然后用 pnpm 重新装一遍,执行 pnpm install。
你会发现它打印了这样一句话:
预览
包是从全局 store 硬连接到虚拟 store 的,这里的虚拟 store 就是 node_modules/.pnpm。
只要是在同一台机器下,下次安装依赖的时候pnpm会先检查store目录,如果有你需要安装的依赖则会通过一个硬链接到你的项目中去,而不是重新安装依赖。这也就表明为什么pnpm性能这么突出了,最大程度的节省了时间消耗和磁盘空间。

我们打开 node_modules 看一下:
在这里插入图片描述

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

展开 .pnpm 看一下:
预览

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

比如 .pnpm 下的 expresss,这些都是软链接,
在这里插入图片描述

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

举例说明:

  1. 项目依赖foo@1.0.0
node_modules
├─ .pnpm
│   └─ foo@1.0.0
│        └─ node_modules
│            └─ foo -> <store>/foo
│                ├─ index.js
│                └─ package.json
│
└─ foo
  1. 项目依赖了foo@1.0.0、bar@1.0.0,bar也依赖了foo@1.0.0
node_modules
├─foo -> ./.pnpm/foo@1.0.0/node_modules/foo
├─bar -> ./.pnpm/bar@1.0.0/node_modules/bar
└─.pnpm├─ bar@1.0.0│   └─ node_modules│       ├─ foo -> ../../foo@1.0.0/node_modules/foo│       └─ bar -> <store>/bar└─ foo@1.0.0└─ node_modules└─ foo -> <store>/foo

为什么需要同软链接的方式去引用实际的依赖呢?其实这样设计的目的是解决“幽灵依赖”问题,只有声明过的依赖才会以软链接的形式出现在node_modules目录中,在实际项目中引用的是软链接,软链接指向的是 .pnpm 的真实依赖,所以在日常开发中不会引用到未在 package.json 声明的包。

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

预览

这就是 pnpm 的实现原理。
那么回过头来看一下,pnpm 为什么优秀呢?
首先,最大的优点是节省磁盘空间呀,一个包全局只保存一份,剩下的都是软硬连接,这得节省多少磁盘空间呀。
其次就是快,因为通过链接的方式而不是复制,自然会快。
这也是它所标榜的优点:

预览

相比 npm2 的优点就是不会进行同样依赖的多次复制。
相比 yarn 和 npm3+ 呢,那就是没有幽灵依赖,也不会有没有被提升的依赖依然复制多份的问题。
这就已经足够优秀了,对 yarn 和 npm 可以说是降维打击。
Workspace
现代前端工程中居多都是使用 Lerna 管理 monorepo 类型的项目,每个人都清楚它的作用,而 pnpm 也是对此进行了友好的支持。与 Lerna 不同的是 pnpm 使用特殊的包选择器语法限制命令,不像 Lerna 那样需要很长难记的命令去标识。

一个 monorepo 工程,目录中必须要拥有管理工作区的配置文件(pnpm.workspace.yaml),相比其它包管理工具的工作区文件其实都大同小异。

packages:# 所有在 packages/ 和 components/ 子目录下的 package- 'packages/**'- 'components/**'# 不包括在 test 文件夹下的 package- '!**/test/**'

一些常用于管理 monorepo 的命令:
● 精确选择一个 repo <@scope/package>,或选择一组 repo <@scope/*>,再或者相对路径选择。

pnpm dev --filter @byted-ehi/basic-list
pnpm dev --filter apps/*
pnpm dev --filter ./apps/admin-order-manage

● 选择一个 repo 以及所属 repo 的依赖项,例如:会运行 basic-list 下的所有依赖的 dev。这个命令的意思是在 @byted-ehi/basic-list 包的所有子目录以及这些子目录中的所有文件中执行开发脚本。

pnpm dev --filter @byted-ehi/basic-list...

● 只选择某个 repo 的依赖项,与上面的区别是不包含 repo。例如:会运行 repo 下所有依赖的 dev,不包含repo 本身。这个命令的意思是在 @byted-ehi/basic-list 包的所有子目录中执行开发脚本。

pnpm dev --filter @byted-ehi/basic-list^...

● 选择指定目录下的所有 repo。

pnpm dev --filter ./apps

总结
pnpm 最近经常会听到,可以说是爆火。本文我们梳理了下它爆火的原因:
npm2 是通过嵌套的方式管理 node_modules 的,会有同样的依赖复制多次的问题。
npm3+ 和 yarn 是通过铺平的扁平化的方式来管理 node_modules,解决了嵌套方式的部分问题,但是引入了幽灵依赖的问题,并且同名的包只会提升一个版本的,其余的版本依然会复制多次。
pnpm 则是用了另一种方式,不再是复制了,而是都从全局 store 硬连接到 node_modules/.pnpm,然后之间通过软链接来组织依赖关系。
这样不但节省磁盘空间,也没有幽灵依赖问题,安装速度还快,从机制上来说完胜 npm 和 yarn。
pnpm 就是凭借这个对 npm 和 yarn 降维打击的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784383.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

统计子矩阵(前缀和+双指针)

题目描述 给定一个 N M 的矩阵 A&#xff0c;请你统计有多少个子矩阵 (最小 1 1&#xff0c;最大 N M) 满足子矩阵中所有数的和不超过给定的整数 K? 输入格式 第一行包含三个整数 N, M 和 K. 之后 N 行每行包含 M 个整数&#xff0c;代表矩阵 A. 输出格式 一个整数…

Django DRF视图

文章目录 一、DRF类视图介绍APIViewGenericAPIView类ViewSet类ModelViewSet类重写方法 二、Request与ResponseRequestResponse 参考 一、DRF类视图介绍 在DRF框架中提供了众多的通用视图基类与扩展类&#xff0c;以简化视图的编写。 • View&#xff1a;Django默认的视图基类&…

ES的RestClient相关操作

ES的RestClient相关操作 Elasticsearch使用Java操作。 本文仅介绍CURD索引库和文档&#xff01;&#xff01;&#xff01; Elasticsearch基础&#xff1a;https://blog.csdn.net/weixin_46533577/article/details/137207222 Elasticsearch Clients官网&#xff1a;https://ww…

(文章复现)考虑分布式电源不确定性的配电网鲁棒动态重构

参考文献&#xff1a; [1]徐俊俊,吴在军,周力,等.考虑分布式电源不确定性的配电网鲁棒动态重构[J].中国电机工程学报,2018,38(16):4715-47254976. 1.摘要 间歇性分布式电源并网使得配电网网络重构过程需要考虑更多的不确定因素。在利用仿射数对分布式电源出力的不确定性进行合…

博客页面---前端

目录 主页 HTML CSS 文章详细页面 HTML CSS 登录页面 HTML CSS 文章编辑页 HTML CSS 这只是前端的页面组成&#xff0c;还没有接入后端&#xff0c;并不是完全体 主页 HTML <!DOCTYPE html> <!-- <html lang"en"> --> <head>&…

区间预测 | Matlab实现带有置信区间的BP神经网络时间序列未来趋势预测

区间预测 | Matlab实现带有置信区间的BP神经网络时间序列未来趋势预测 目录 区间预测 | Matlab实现带有置信区间的BP神经网络时间序列未来趋势预测预测效果基本介绍研究回顾程序设计参考资料预测效果 基本介绍 BP神经网络(Backpropagation neural network)是一种常用的人工神…

DisplayPort 的演变

HDMI 2.0的传输带宽18Gbit/s; DP 1.2 的传输带宽17.28Gbit/s理论上HDMI 2.0高一点&#xff0c;实际上没区别.。 HDMI接口和DP接口的区别 1、厂商不同HDMI是电视机厂商主导的,而DP是由PC及芯片制造商联盟开发的.需要注意的是,HDMI需要授权费,DP则不需要. 2、版本进化。 2006 年…

http模块 设置资源类型(mime类型)

虽然浏览器自带websocket功能它会根据响应回来的内容自动去判断资源类型&#xff0c;但是我们加上了mime类型判断代码会更加规范些 一、mime类型概念&#xff1a; 媒体类型是一种标准&#xff0c;它用来表示文档。文件、字节流的性质和格式。HTTP服务可以设置响应头Content-T…

【InternLM 实战营第二期笔记】InternLM1.8B浦语大模型趣味 Demo

体验环境 平台&#xff1a;InternStudio GPU&#xff1a;10% 配置基础环境 studio-conda -o internlm-base -t demo 与 studio-conda 等效的配置方案 conda create -n demo python3.10 -y conda activate demo conda install pytorch2.0.1 torchvision0.15.2 torchaudio2…

Tidb和MySQL性能简单测试对比

一、单SQL性能对比 由于TiDB的并发能力优秀&#xff0c;但是单个SQL执行延迟较差&#xff0c;为了客观对比&#xff0c;所以只用1个线程来压测tidb和mysql&#xff0c;以观察延迟情况 二、并发SQL性能对比 TiDB:v6.5.2 MySQL:8.0.26 &#xff08;单机&#xff09; 三、结论 …

155 Linux C++ 通讯架构实战10,工具telent 和 wireshark的使用

telnet工具使用介绍 windows 上开启telnet linux 上开始telnet 使用telnet //是一款命令行方式运行的客户端TCP通讯工具&#xff0c;可以连接到服务器端&#xff0c;往服务器端发送数据&#xff0c;也可以接收从服务器端发送过来的信息&#xff1b; //类似nginx5_1_1_clie…

上位机图像处理和嵌入式模块部署(qmacvisual形状匹配)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在qmacvisual软件当中&#xff0c;提供了两种模板匹配的方法。除了前面介绍的灰度匹配&#xff0c;就是今天讲的形状匹配。当然&#xff0c;对于使…

嵌入式linux学习之opencv交叉编译

1.下载opencv源码 OpenCV官方源码下载链接为https://opencv.org/releases/&#xff0c;选择3.4.16版本下载。放在ubuntu系统~/opencv文件夹中&#xff0c;解压缩&#xff0c;opencv文件夹中新建build和install文件夹用于存放编译文件和安装文件&#xff1a; 2. 安装编译工具…

设计模式学习笔记 - 设计模式与范式 -行为型:2.观察者模式(下):实现一个异步非阻塞的EventBus框架

概述 《1.观察者模式&#xff08;上&#xff09;》我们学习了观察者模式的原理、实现、应用场景&#xff0c;重点节介绍了不同应用场景下&#xff0c;几种不同的实现方式&#xff0c;包括&#xff1a;同步阻塞、异步非阻塞、进程内、进程间的实现方式。 同步阻塞最经典的实现…

最优算法100例之18-列升序行升序的数组中查找元素

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样一…

AI绘画教程:Midjourney使用方法与技巧从入门到精通

文章目录 一、《AI绘画教程&#xff1a;Midjourney使用方法与技巧从入门到精通》二、内容介绍三、作者介绍&#x1f324;️粉丝福利 一、《AI绘画教程&#xff1a;Midjourney使用方法与技巧从入门到精通》 一本书读懂Midjourney绘画&#xff0c;让创意更简单&#xff0c;让设计…

[图像处理] MFC载入图片并进行二值化处理和灰度处理及其效果显示

文章目录 工程效果重要代码完整代码参考 工程效果 载入图片&#xff0c;并在左侧显示原始图片、二值化图片和灰度图片。 双击左侧的图片控件&#xff0c;可以在右侧的大控件中&#xff0c;显示双击的图片。 初始画面&#xff1a; 载入图片&#xff1a; 双击左侧的第二个控件…

随便注【强网杯2019】

大佬的完整wp&#xff1a;buuctf-web-[强网杯 2019]随便注-wp_取材于某次真实环境渗透,只说一句话:开发和安全缺一不可-CSDN博客 知识点&#xff1a; 单引号字符型绕过堆叠注入 可以执行多条语句multi_query()&#xff1a;该函数可能引发堆叠注入handler用法 mysql专属&#…

Linux之进程间通信

1.进程间通信的目的 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享&#xff1a;多个进程之间共享同样的资源。 通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通知它&#xff08;它们&#xff09;发生了某种事件&#xff…

Rabbit简单模式理解

简单模式 我们以最普通的方式去理解&#xff0c;并没有整合Springboot的那种 这是最简单的模式&#xff0c;一个生产者&#xff0c;一个消费者&#xff0c;一个队列 测试 1、 导包&#xff0c;没整合&#xff0c;不需要编写配置 2、需要生产者消费者 导包 <dependency…