pnpm才是前端工程化项目的未来
前言
相信小伙伴们都接触过npm/yarn,这两种包管理工具想必是大家工作中用的最多的包管理工具,npm作为node官方的包管理工具,它是随着node的诞生一起出现在大家的视野中,而yarn的出现则是为了解决npm带来的诸多问题,虽然yarn提高了依赖包的安装速度与使用体验,但它依旧没有解决npm的依赖重复安装等致命问题。pnpm的出现完美解决了依赖包重复安装的问题,并且实现了yarn带来的所有优秀体验,所以说pnpm才是前端工程化项目的未来。
如果这篇文章有帮助到你,️关注+点赞️鼓励一下作者,文章公众号首发,关注 前端南玖 第一时间获取最新文章~
npm 与 yarn 存在的问题
早期的npm
在npm@3之前,node_modules结构可以说是整洁、可预测的,因为当时的依赖结构是这样的:
node_modules
└─ 依赖A
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖B
├─ index.js
└─ package.json
└─ 依赖C
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖B
├─ index.js
└─ package.json
每个依赖下面都维护着自己的node_modules,这样看起来确实非常整洁,但同时也带来一些较为严重的问题:
- 依赖包重复安装
- 依赖层级过多
- 模块实例无法共享
依赖包重复安装
从上面的依赖结构我们可以看出,依赖A与依赖C同时引用了依赖B,此时的依赖B会被下载两次。此刻我们想想要是某一个依赖被引用了n次,那么它就需要被下载n次。(此时心里是不是在想,怎么会有如此坑的设计)
依赖层级过多
我们再来看另外一种依赖结构:
node_modules
└─ 依赖A
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖B
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖C
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖D
├─ index.js
└─ package.json
这种依赖层级少还能接受,要是依赖层级多了,这样一层一层嵌套下去,就像一个依赖地狱,不利于维护。
npm@3与yarn
为了解决上述问题,npm3与yarn都选择了扁平化结构,也就是说现在我们看到的node_modules里面的结构不再有依赖嵌套了,都是如下依赖结构:
node_modules
└─ 依赖A
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖C
├─ index.js
├─ package.json
└─ node_modules
└─ 依赖B
├─ index.js
├─ package.json
└─ node_modules
node_modules下所有的依赖都会平铺到同一层级。由于require寻找包的机制,如果A和C都依赖了B,那么A和C在自己的node_modules中未找到依赖C的时候会向上寻找,并最终在与他们同级的node_modules中找到依赖包C。 这样就不会出现重复下载的情况。而且依赖层级嵌套也不会太深。因为没有重复的下载,所有的A和C都会寻找并依赖于同一个B包。自然也就解决了实例无法共享数据的问题
由于这个扁平化结构的特点,想必大家都遇到了这样的体验,自己明明就只安装了一个依赖包,打开node_modules文件夹一看,里面却有一大堆。
这种扁平化结构虽然是解决了之前的嵌套问题,但同时也带来了另外一些问题:
- 依赖结构的不确定性
- 扁平化算法的复杂度增加
- 项目中仍然可以非法访问没有声明过的依赖包(幽灵依赖)
依赖结构的不确定性
这个怎么理解,为什么会产生这种问题呢?我们来仔细想想,加入有如下一种依赖结构:
A包与B包同时依赖了C包的不同版本,由于同一目录下不能出现两个同名文件,所以这种情况下同一层级只能存在一个版本的包,另外一个版本还是要被嵌套依赖。
那么问题又来了,既然是要一个扁平化一个嵌套,那么这时候是如何确定哪一个扁平化哪一个嵌套的呢?
这两种结构都有可能,准确点说哪个版本的包被提升,取决于包的安装顺序!
这就是为什么会产生依赖结构的不确定问题,也是 lock 文件诞生的原因,无论是package-lock.json(npm 5.x 才出现)还是yarn.lock,都是为了保证 install 之后都产生确定的node_modules结构。
尽管如此,npm/yarn 本身还是存在扁平化算法复杂和package 非法访问的问题,影响性能和安全。
pnpm
前面说了那么多的npm与yarn的缺点,现在再来看看pnpm是如何解决这些尴尬问题的。
什么是pnpm
快速的,节省磁盘空间的包管理工具
就这么简单,说白了它跟npm与yarn没有区别,都是包管理工具。但它的独特之处在于:
- 包安装速度极快
- 磁盘空间利用非常高效
特性
安装包速度快
从上图可以看出,pnpm的包安装速度明显快于其它包管理工具。那么它为什么会比其它包管理工具快呢?
我们来可以来看一下各自的安装流程
- npm/yarn
resolving:首先他们会解析依赖树,决定要fetch哪些安装包。
fetching:安装去fetch依赖的tar包。这个阶段可以同时下载多个,来增加速度。
wrting:然后解压包,根据文件构建出真正的依赖树,这个阶段需要大量文件IO操作。
- pnpm
上图是pnpm的安装流程,可以看到针对每个包的三个流程都是平行的,所以速度会快很多。当然pnpm会多一个阶段,就是通过链接组织起真正的依赖树目录结构。
磁盘空间利用非常高效
pnpm 内部使用基于内容寻址的文件系统来存储磁盘上所有的文件,这个文件系统出色的地方在于:
- 不会重复安装同一个包。用 npm/yarn 的时候,如果 100 个项目都依赖 lodash,那么 lodash 很可能就被安装了 100 次,磁盘中就有 100 个地方写入了这部分代码。但在使用 pnpm 只会安装一次,磁盘中只有一个地方写入,后面再次使用都会直接使用
hardlink。 - 即使一个包的不同版本,pnpm 也会极大程度地复用之前版本的代码。举个例子,比如 lodash 有 100 个文件,更新版本之后多了一个文件,那么磁盘当中并不会重新写入 101 个文件,而是保留原来的 100 个文件的
hardlink,仅仅写入那一个新增的文件。
支持monorepo
pnpm 与 npm/yarn 另外一个很大的不同就是支持了 monorepo,pnpm内置了对monorepo的支持,只需在工作空间的根目录创建pnpm-workspace.yaml和.npmrc配置文件,同时还支持多种配置,相比较lerna和yarn workspace,pnpm解决monorepo的同时,也解决了传统方案引入的问题。
monorepo 的宗旨就是用一个 git 仓库来管理多个子项目,所有的子项目都存放在根目录的
packages目录下,那么一个子项目就代表一个package。
依赖管理
pnpm使用的是npm version 2.x类似的嵌套结构,同时使用.pnpm 以平铺的形式储存着所有的包。然后使用Store + Links和文件资源进行关联。简单说pnpm把会包下载到一个公共目录,如果某个依赖在 sotre 目录中存在了话,那么就会直接从 store 目录里面去 hard-link,避免了二次安装带来的时间消耗,如果依赖在 store 目录里面不存在的话,就会去下载一次。通过Store + hard link的方式,使得项目中不存在NPM依赖地狱问题,从而完美解决了npm3+和yarn中的包重复问题。
我们分别用npm与pnpm来安装vite对比看一下
| npm | pnpm |
|---|---|
所有依赖包平铺在node_modules目录,包括直接依赖包以及其他次级依赖包 |
node_modules目录下只有.pnpm和直接依赖包,没有其他次级依赖包 |
| 没有符号链接(软链接) | 直接依赖包的后面有符号链接(软链接)的标识 |
pnpm安装的vite 所有的依赖都软链至了 node_modules/.pnpm/ 中的对应目录。 把 vite 的依赖放置在同一级别避免了循环的软链。
软链接 和 硬链接 机制
pnpm 是通过 hardlink 在全局里面搞个 store 目录来存储 node_modules 依赖里面的 hard link 地址,然后在引用依赖的时候则是通过 symlink 去找到对应虚拟磁盘目录下(.pnpm 目录)的依赖地址。
这两者结合在一起工作之后,假如有一个项目依赖了 A@1.0.0 和 B@1.0.0 ,那么最后的 node_modules 结构呈现出来的依赖结构可能会是这样的:
node_modules
└── A // symlink to .pnpm/A@1.0.0/node_modules/A
└── B // symlink to .pnpm/B@1.0.0/node_modules/B
└── .pnpm
├── A@1.0.0
│ └── node_modules
│ └── A -> <store>/A
│ ├── index.js
│ └── package.json
└── B@1.0.0
└── node_modules
└── B -> <store>/B
├── index.js
└── package.json
node_modules 中的 A 和 B 两个目录会软连接到 .pnpm 这个目录下的真实依赖中,而这些真实依赖则是通过 hard link 存储到全局的 store 目录中。
store
pnpm下载的依赖全部都存储到store中去了,store是pnpm在硬盘上的公共存储空间。
pnpm的store在Mac/linux中默认会设置到{home dir}>/.pnpm-store/v3;windows下会设置到当前盘符的根目录下。使用名为 .pnpm-store的文件夹名称。
项目中所有.pnpm/依赖名@版本号/node_modules/下的软连接都会连接到pnpm的store中去。
我是南玖,我们下期见!!!
pnpm才是前端工程化项目的未来的更多相关文章
- webpack4.x + vue2.x 构建前端工程化(1)
本篇文篇纯属个人笔记,实现工程化打包(用打包后的文件可以正常渲染页面),后续继续更新配置开发环境与生产环境,如果有不合理的地方还望各位指点! 不用脚手架,直接用vue和webpack搭建前端工程化项目 ...
- 传统项目转前端工程化——路由跳转时出现浏览器锁死和白屏【该死的同步ajax】
[一开始我想到是该死的同步ajax,但我没验证,把他忽略了] 在探索前端工程化vue-cli做spa时,从搜索结果页跳转商品详情页时,因为详情页有很多ajax请求,并且都用的同步请求,就会导致请求时浏 ...
- 公司内部技术分享之Vue.js和前端工程化
今天主要的核心话题是Vue.js和前端工程化.我将结合我这两年多的工作学习经历来谈谈这个,主要侧重点是前端工程化,Vue.js侧重点相对前端工程化,比重不是特别大. Vue.js Vue.js和Rea ...
- 前端工程化 - 剖析npm的包管理机制
转自https://juejin.im/post/5df789066fb9a0161f30580c 现如今,前端开发的同学已经离不开 npm 这个包管理工具,其优秀的包版本管理机制承载了整个繁荣发展的 ...
- 页面仔初窥"前端工程化"
今天看了几篇前端界的一位大牛--张云龙的文章,其中一篇在自己的理解范围内看得懂一些,有所收获,说的是前端工程化的事,看完算是对前端工程形成了一个模糊的概念. 现在我所接触到的前端开发,还是张云龙大神所 ...
- 10分钟学会前端工程化(webpack4.0)
一.概要 1.1.前端工程化 随着前端的不断发展与壮大,前端变得越来越复杂,组件化.模块化.工程化.自动化成了前端发展中不可或缺的一部分,具体到前端工程化,面临的问题是如何提高编码->测试-&g ...
- 基于webpack的前端工程化开发解决方案探索(二):代码分割与图片加载
今天我们继续来进行webpack工程化开发的探索! 首先来验证上一篇文章 基于webpack的前端工程化开发解决方案探索(一):动态生成HTML 中的遗留问题:webpack将如何处理按需加载的 ...
- 前端工程化 Webpack基础
前端工程化 模块化 (js模块化,css模块化,其他资源模块化) 组件化 (复用现有的UI结构.样式.行为) 规范化 (目录结构的划分.编码规范化.接口规范化.文档规范化.Git分支管理) 自动化 ( ...
- 前端工程化开发之yeoman、bower、grunt
上两遍文章介绍了前端模块化开发(以seaJs为例)和前端自动化开发(以grunt为例)的流程,参见: http://www.cnblogs.com/luozhihao/p/4818782.html ( ...
- 基于webpack的前端工程化开发解决方案探索(一):动态生成HTML(转)
1.什么是工程化开发 软件工程的工程化开发概念由来已久,但对于前端开发来说,我们没有像VS或者eclipse这样量身打造的IDE,因为在大多数人眼中,前端代码无需编译,因此只要一个浏览器来运行调试就行 ...
随机推荐
- Golang数据结构
数据类型 不同类型的内存样式图 append,切片添加元素 清空切片的3种方法 清空切片的2种方法 查看变量类型 使用 fmt.Printf package main import "fmt ...
- Teamcenter_NX集成开发:通过NXOpen查询零组件是否存在
之前用过NXOpen PDM的命名空间下的类,现在记录一下通过PDM命名空间下的类查询Teamcenter零组件的信息,也可以用来判断该零组件是否存在. 1-该工程为DLL工程,直接在NX界面调用,所 ...
- 【ACM算法竞赛日常训练】DAY5题解与分析【储物点的距离】【糖糖别胡说,我真的不是签到题目】| 前缀和 | 思维
DAY5共2题: 储物点的距离(前缀和) 糖糖别胡说,我真的不是签到题目(multiset,思维) 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的方式讲解算法 ...
- ArcGIS倾斜摄影无法加载找不到nodes节点
倾斜摄影无法加载,在fiddler请求中,nodes/root请求失败,如:http://10.0.7.173:6080/arcgis/rest/services/Hosted/xm4490/Scen ...
- 【Deep Learning】DDPM
DDPM 1. 大致流程 1.1 宏观流程 1.2 训练过程 1.3 推理过程 2. 对比GAN 2.1 GAN流程 2.2 相比GAN优点 训练过程更稳定,损失函数指向性更强(loss数值大小指示训 ...
- python之sys库
sys --- 系统相关的参数和函数 该模块提供了一些变量和函数.这些变量可能被解释器使用,也可能由解释器提供.这些函数会影响解释器.本模块总是可用的. sys.abiflags 在POSIX系统上, ...
- 大规模 Transformer 模型 8 比特矩阵乘简介 - 基于 Hugging Face Transformers、Accelerate 以及 bitsandbytes
引言 语言模型一直在变大.截至撰写本文时,PaLM 有 5400 亿参数,OPT.GPT-3 和 BLOOM 有大约 1760 亿参数,而且我们仍在继续朝着更大的模型发展.下图总结了最近的一些语言模型 ...
- 机器学习06-(支持向量机SVM、网格搜索、文本分词、词袋模型、词频、文本分类-主题识别)
机器学习-06 机器学习-06 支持向量机(SVM) 支持向量机原理 网格搜索 情感分析 文本分词 词袋模型 词频(TF) 文档频率(DF) 逆文档频率(IDF) 词频-逆文档频率(TF-IDF) 文 ...
- cryptohack wp day(3)
第二节模运算----第一题( GCD ) 在做这道题前,了解下欧几里得算法: 欧几里得算法,也叫辗转相除法,用于求解两个非负整数a和b的最大公约数(Greatest Common Divisor, G ...
- 2022-02-10:k8s安装mongo,yaml如何写?
2022-02-10:k8s安装mongo,yaml如何写? 答案2022-02-10: yaml如下: apiVersion: v1 kind: Service metadata: labels: ...