pnpm--他简直是超人!

引言

pnpm是什么?pnpm比npm多个p,比ppnpm少个p,比pppnpm少两个p。。。。好,言归正传,pnpm的p其实是power的意思,意思是pnpm的超级版,超级在哪呢?

诞生背景

pnpm 的诞生源于对现有包管理工具(如 npm 和 Yarn)在性能和资源利用方面的不足的改进需求:

  1. 磁盘空间浪费 :npm 和 Yarn 在安装依赖时会复制所有文件到每个项目的 node_modules,导致重复依赖浪费磁盘空间。
  2. 安装速度慢:传统工具的模块安装速度较慢,尤其是在大型项目中,存在大量重复下载和处理。
  3. 依赖管理问题 :扁平化的 node_modules 结构可能导致依赖冲突,而解决这些冲突需要额外的时间和精力。

pnpm 由 Zoltan Kochan 在 2016 年推出,利用符号链接和硬链接技术,实现共享依赖库,从根本上优化磁盘使用,并显著提升安装速度和一致性,成为现代前端开发的一项重要工具。

总而言之,npm和yarn太拉了,Zoltan Kochan 看不下去了,要把他俩干趴下

了解pnpm的前置知识

硬链接

听名字就知道,很硬,很靠得住,硬在哪?他直接指向硬盘里的文件!炸不炸裂?他直接指向硬盘又说明什么呢?举个例子:假如说我有三个硬链接a,b,c他们都指向某个数据结构,我们删除a,b和c还是指向那块数据结构,再删掉b,c还是指向那块数据结构,只有把所有硬链接都删掉的时候,这个数据结构才从逻辑上被删除了,为什么说逻辑上被删除了?其实物理上,他依旧存在硬盘上(总不可能直接把硬盘那一块扣掉吧),只是没有链接指向他了,这无关紧要,他无法被使用了,就相当于他被删除了,在操作系统的算法中他就是空白,只能等待被其他数据结构覆盖的命运了。

软连接(也叫符号连接)

软连接比硬链接稍微软点,硬链接比软连接稍微硬点。。。。软在哪?他是通过指向目标文件的目录实现的,假如a,b,c都是指向某数据结构的软链接,实际上这三个链接都是指向这个数据结构的路径,我把a删了,操作系统就会把路径删了,因为b和c都是通过该路径指向目标数据结构的,所以b和c也会随之实效。看他这怂样就不好用,它存在的意义是什么?原来因为协议和管理方式的不同,硬链接不能跨文件系统和跨盘指向,而软连接可以,从某种角度来看,软连接就是硬连接的代餐,实在不行才会用他

pnpm-lock.yaml

这个可以讲的非常多,可以参考我另外一篇文章:pnpm.lock.yaml,看似无关紧要,实则······

与yarn和npm的不同之处

存储结构

开发项目时,npm和yarn会把安装的包放在该项目的node_modules目录下,假如有两个项目处于同一文件目录,并且都依赖了一个包,那么npm或者yarn就会把该包放到父文件的node_modules中(父目录有package.json,该依赖包版本一致,hoist机制开启的话),这两个项目直接引用父目录的node_modules,然后两个包的体积就变成了一个包的体积,牛不牛B?这只能算小牛B,hoist有自己的问题:Yarn 和 npm 的 hoist 机制在遇到版本冲突时,可能导致某些子项目依赖被覆盖,或者需要存储多个版本。更牛B的是pnpm的存储结构,下载依赖时,他直接把依赖放在了全局统一的目录中(~/.pnpm-store)中,项目中使用该依赖的时候,直接用硬链接或者软连接指向全局存储并使用,当其他子项目再安装这个依赖的时候,回去检查全局目录有没有这个依赖,如果有,直接就指向这个依赖,连安装的时间都省了,还节省空间。hoist机制使npm和yarn能在特定的条件下合并共享依赖 ,而pnpm直接就在全局中存储依赖,只要版本一致,整个设备都不用安装第二次依赖,高下立判。

干掉幽灵依赖!

NPM 和 Yarn 的行为
  1. 安装依赖和声明的关联

    • 在使用 npm或yarn 安装依赖时:

      sql 复制代码
      npm install left-pad
      yarn add left-pad

      它们会将 left-pad添加到 package.json的 dependencies中。

    • 但是,如果你直接运行某些代码使用了未在 package.json 中声明的依赖(即这些依赖是通过其他包的间接依赖引入的),npmyarn 不会阻止你使用它们。这就导致了幽灵依赖问题。

  2. 问题根源

    • 因为 NPM 和 Yarn 使用的是扁平化依赖树 ,即所有的依赖都被放在 node_modules 的根目录下(只要版本不冲突)。这样,某个包的子依赖可能意外变得"可见",即使它没有明确声明为直接依赖。
PNPM 的行为
  1. 安装和声明严格对应

    • 使用 pnpm 安装包时,行为类似:

      sql 复制代码
      pnpm add left-pad

      它会将 left-pad 添加到 package.jsondependencies 中。

    • 但与 npmyarn 的不同之处在于,如果依赖未在 package.json 中声明,pnpm 会拒绝让代码访问它,即使它是通过间接依赖安装的。

  2. 严格隔离依赖

    • pnpm 的依赖树是嵌套结构,确保只有直接依赖出现在你的 node_modules 中的顶层,其他子依赖都放在它们各自的 node_modules 中。

    • 因此,如果你尝试导入一个未显式声明的依赖(即幽灵依赖),会出现如下错误:

      arduino 复制代码
      Error: Cannot find module 'left-pad'
  3. 总结对比

    • NPM 和 Yarn:即使包未声明为直接依赖,只要它通过间接方式安装,代码仍然可以访问(容易忽略声明)。
    • PNPM :严格要求 package.json 中声明依赖,未声明的包即使安装了也不可访问,完全避免幽灵依赖。

太多专业术语虽然严谨,却不好理解,卓卓贴心的安排了讲人话环节。

讲人话环节:npm和yarn是安装了包才会在package中声明,有时候会忽略声明,而pnpm是package.json中没有声明就不会安装包,package.json也是好起来了,农奴翻身把歌唱,从被决定的变成了决定的。

pnpm的hoist--脱裤子放屁?

通过上面的解释,我们可以知道,项目node_modules的依赖只是个硬链接,占据的空间本来就是可以忽略不计,那把他提升到父项目的node_modules是闹哪样?兼职和蒙古海军一样没用!。。。其实有点用,但不多

PNPM Hoist 的实际用途
  1. 解决旧代码或工具兼容性问题

    • 某些遗留项目或工具(如部分 Webpack 插件、Babel 配置)假定依赖可以从顶层直接访问。如果使用 PNPM 的默认嵌套结构,这些工具可能报错。
    • Hoist 可以在特定情况下恢复兼容性。
  2. 减少重复依赖

    • 在大规模项目中(尤其是 Monorepo),某些依赖可能被多个子项目反复安装。Hoist 可以将这些公共依赖提升到顶层,从而减少 node_modules 的深度和重复。
  3. 加快开发工具的运行速度

    • 某些开发工具(如 LSP、TS Server)在扫描 node_modules 时,面对深层次嵌套目录可能变慢。
    • Hoist 可以减少扫描的复杂性,提高运行效率。

人话环节

有些老依赖是根据npm项目和yarn项目的结构设计的,并且没有为pnpm特定再设计一个版本,而hoist可以使 pnpm项目结构更加'返祖',能获得更好的兼容性。

总结

pnpm简直完美,要我评价就是马中赤兔,人中卓卓!但是他还有一些不足之处,他毕竟太'年轻'了,自然就会有生态不如老包管理工具的问题存在,就像上面说的,明明很鸡肋的hoist却能在pnpm中被保留就是为了解决部分兼容性问题,但是以pnpm超强的性能,使用它的人肯定会越来越多,对生态系统支持的兼容性问题被解决只是时间问题。

相关推荐
@大迁世界7 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路16 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug19 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213821 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中43 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端