pnpm 管理依赖包是如何节省磁盘空间的?
npm 存在的问题
我们经常使用 npm 来管理 node 项目中的包,从 package.json 中读取配置将依赖下载到本地,以保障项目的正常运行。
当项目数量多时,这样的包管理方式会非常的占用电脑内存。由于每个项目都有属于自己的依赖,每个项目都需要安装,即使 npm 会对依赖进行缓存,但是每个项目仍然需要安装到自己的 node_modules 文件夹下,此时每个项目安装的每一份依赖都会在磁盘中保存一份,即使各个项目中依赖的版本可能相同。
pnpm 就是针对以上问题出现的解决方案,它使用统一的仓库来存放项目中的包,在项目中使用硬链接+软连接的方式找到依赖所在磁盘的位置。

硬链接和软连接
想要清晰的知道 pnpm 管理依赖的原理,首先要了解硬链接和软连接、拷贝操作的区别。
拷贝操作会在磁盘中复制一份新的数据,比如拷贝 a.js 为 a_copy.js,两个文件在拷贝后就互不关联,修改 a.js 不会影响 a_copy.js,删除 a_copy.js 也不会影响 a.js。
硬链接是通过寻址的方式找到磁盘中的数据,比如新建 b_hard.js 与 b.js 创建硬链接,两者指向的是同一个磁盘数据,所以修改其中一个文件,另一个文件也会发生变化。
软连接就是我们平时常见的创建快捷方式(文件后面会存在一个向右的小箭头),它只是保存着文件的路径,不可以编辑,直接双击就会找到原始的文件。如果原文件被删除,通过软连接将无法找到磁盘中的数据。

我们可以通过命令来进行连接操作,windows 是这样的
/*拷贝*/ copy a.js a_copy.js
/*硬链接*/ mklink /H b_hard.js b.js
/*软连接*/ mklink c_soft.js c.js
pnpm原理
使用 npm 或者 yarn 时,如果有100个项目,并且所有项目都有一个相同的依赖包,那么在磁盘上就需要保存100份该相同依赖的包。
如果使用 pnpm,依赖包将被放在统一的位置,当安装包时,其包含的所有文件会硬链接到这个位置,不会另外占用磁盘空间,这样不同项目之间就可以共享相同版本的依赖。
如果对同一依赖包使用相同的版本,那么磁盘上只有这个依赖包的一份文件,如果对同一依赖包使用不同的版本,那么只有版本之间不同的文件被存储起来。
比如 a/b/c 三个项目都使用 axios,axios 的所有文件都保存在 pnpm 上,axios 这些文件对应着磁盘的数据,直接 a/b/c 项目的axios 通过硬链接指向磁盘里的数据。 这样有两个好处:
(1)效率非常高,无需下载、查找缓存解压等操作
(2)节省磁盘空间,每个项目不需要再下载一份
pnpm 依赖包统一保存的位置可以使用命令 pnpm store path 来查看
非扁平的 node_modules 目录
使用 npm 或者 yarn安装的依赖包会将所有的子级依赖全部平铺到 node_modules 文件夹中,即扁平化的目录结构,这样会导致源码可以访问本来不属于当前项目所设定的依赖包。
比如安装 axios ,同时会安装非常多的其它的库如 form-data,虽然在 package.json 中是没有配置的,但在源代码中可以直接通过require('form-data') 引用,这样就会有隐患,如果项目某天删除了 axios,form-data 就不存在了。
使用 npm 和 pnpm 分别只安装 axios,npm 会将 axios 所需的其它依赖平铺,而 pnpm 的 node_modules 根目录下只有 axios 和 .pnpm 文件夹,这样就可以避免非主动下载的其它依赖包可随意访问的情况。

如果直接按照这样的层级下载包,可能会带来新的问题,如多个包依赖同一个包时,就会被重复安装。
▾ node_modules
▾ axios
▾ node_modules
▸ form-data
▾ xxx
▾ node_modules
▸ form-data
那 pnpm 是如何做到非扁平化并且不重复安装的呢?答案就是它使用硬链接与软连接结合的方式来与依赖包关联。
在 node_modules 根目录有一个文件夹 .pnpm,这里包含了项目所有依赖。
根目录下 axios 软连接到 .pnpm 目录下的 axios 文件夹中,展开 .pnpm/axios@16.1 的node_modules 文件夹,其中有 axios 所需的依赖,包含 axios、follow-redirects、form-data、proxy-from-env,其中 axios 硬链接到磁盘中(即与 pnpm 仓库保存的地址一致),其它文件软连接到 .pnpm 的自身位置。

node_modules 根目录下的依赖,软连接到 .pnpm 文件夹中,如果有相互依赖的关系,仍然通过软连接,只有找到依赖自身,才会通过硬链接找到磁盘中的位置,这样可以保证同一个项目里不同依赖也不会重复安装,同时不同项目之间的相同依赖也无需在磁盘中存储多份。
pnpm 管理依赖包是如何节省磁盘空间的?的更多相关文章
- 安装VM-TOOLS,解压tar包时提示目录磁盘空间不足
在虚拟机里安装了ubuntu-18.04.4-desktop-amd64,安装VM-TOOLS,解压tar包时提示目录磁盘空间不足. 解决方法一: 打开terminal,输入:sudo apt ins ...
- golang使用vendor目录来管理依赖包
Vendor目录介绍 随着Go 1.5 release版本的发布,vendor目录被添加到除了GOPATH和GOROOT之外的依赖目录查找的解决方案.在Go 1.6之前,你需要手动的设置环境变量GO1 ...
- go module管理依赖包
go mod 最大的好处就是摆脱了GOPATH这个限制,在除了GOPATH以外的目录下也能开展你的项目 go mod使用: 1,确保你的go版本是1.1以上 2,创建一个项目目录example,并添加 ...
- Vue笔记:使用 Yarn 管理依赖包
上年10月份, Facebook 发布了新的 node.js 包管理器 Yarn 用以替代 npm ,它比npm更快.更高效. Yarn VS npm 1.yarn.lock 文件 在 npm 中同样 ...
- 017-通过govendor管理依赖包
1:安装 go get -u github.com/kardianos/govendor 2:配置环境变量 需要把 $GOPATH/bin/ 加到 PATH 中 D:\my_workspace\go_ ...
- 引入HBase依赖包带来的麻烦
在一个项目里用到HBase做底层存储,使用maven来管理相关Jar包依赖,用maven来管理依赖包,特别不爽的就是他会将你引入Jar包自己的依赖都搞进来,经常会出现一些类和方法冲突找不到等状况.这次 ...
- 有关项目依赖包发生 Manifest Merge 冲突的详细解决方案
安卓开发使用 Gradle 插件管理依赖包确实非常方便,尤其是在解决一些依赖冲突的问题上.比如,重复依赖的问题,具体内容请我之前写的一篇文章: 有关 Android Studio 重复引入包的问题和解 ...
- go mod 无法自动下载依赖包的问题
go 11以后启用了go mod功能,用于管理依赖包. 当执行go mod init生成go.mod文件之后,golang在运行.编译项目的时候,都会检查依赖并下载依赖包. 在启动了go mod之后, ...
- 为什么在SpringBoot+maven的项目中,所引入的依赖包可以不指定依赖的版本号?
当在Springboot项目中引入了spring-boot-starter-parent,则可以不用引入依赖包版本号,比如: <parent> <groupId>org.spr ...
- Elasticsearch 5.0 磁盘空间节省策略的认识
前言:本文是当时QQ群员讨论磁盘空间如何优化,我搜了下类似的文章,结合官方文档做了一些总结 参考文章1 参考文章2 如果你有疑问,可以联系我参与讨论,或者去原文查看. NOTE: 磁盘空间节省问题,是 ...
随机推荐
- javascript报错: TypeError: (0 , _api_music.default) is not a function
报错截图 错误原因 从其他文件引入变量时,未添加花括号 错误写法 解决方案 效果图 至此问题解决
- @ControllerAdvice 注解使用及原理探究
最近在新项目的开发过程中,遇到了个问题,需要将一些异常的业务流程返回给前端,需要提供给前端不同的响应码,前端再在次基础上做提示语言的国际化适配.这些异常流程涉及业务层和控制层的各个地方,如果每个地方都 ...
- 解读 --- Span<T>
引言 Span<T> 是C# 中的一种结构体,它是一种内存安全的类型,可以用来表示连续的内存区域.Span<T> 可以被用于访问和操作数组.堆上分配的内存和栈上分配的内存.使用 ...
- 终于搞懂了python2和python3的encode(编码)与decode(解码)
终于搞懂了python2的编码 在python2下碰到非常多次的中文乱码,这次来梳理一下编码问题. 在python 2中默认编码是 ASCII,而在python 3中默认编码是 unicode. un ...
- CI+JUnit5并发单测机制创新实践
一. 现状·问题 针对现如今高并发场景的业务系统,"并发问题" 终归是必不可少的一类(占比接近10%),每次出现问题和事故后,需要耗费大量人力成本排查分析并修复.那如果能在事前尽可 ...
- 让 GPT-4 来修复 Golang “数据竞争”问题(续) - 每天5分钟玩转 GPT 编程系列(7)
目录 1. 我以为 2. 阴魂不散的"数据竞争"问题 3. 老规矩,关门放 GPT-4 3.1 复现问题 3.2 让 GPT-4 写一个单元测试 3.3 修复 Wait() 中的逻 ...
- AgileConfig-1.7.0 发布,支持 SSO 🎉🎉🎉
AgileConfig 已经好久好久没有更新过比较大的功能了.一是 AgileConfig 本身的定位就是比较轻量,不想集成太多的功能.二是比较忙(懒).但是本次升级给大家带来了一个比较有用的功能 S ...
- .NET Core WebAPI 基础 文件上传
昨天分享了一个在WebApi中如何接收参数的文章 .NET API 中的 FromRoute.FromQuery.FromBody 用法 - 一事冇诚 - 博客园 (cnblogs.com),然后有新 ...
- 给你的 SpringBoot 工程部署的 jar 包瘦瘦身吧!
之前有写过一篇有关maven插件的文章:spring-boot-maven-plugin插件详解 一.需求背景 我们知道Spring Boot项目,是可以通过java -jar 包名 启动的. 那为什 ...
- 在 Net7.0环境下通过反射创建泛型实例和调用泛型方法
一.介绍 最近没事干,就用闲暇时间写点东西,也记录一下温习历程.老人说的好,好记性,不如烂笔头.时间一长,当时记忆的再清楚,都会变得模糊,索性就写博客记录下来,如果下次需要,直接打开博客就找到了,不用 ...