背景
某一天逛Github的时候发现有个项目叫pnpm,官方的描述以及bechmarks如下
Fast, disk space efficient package manager -- 快速的,节省磁盘空间的包管理工具
这么牛*的么?赶紧学习起来
特性
官方总结了pnpm的4个特性:Fast、Efficient、Supports monorepos、Strict
一个个看下分别是咋实现的
Fast
pnpm is up to 2x faster than the alternatives
hard link & soft link
使用pnpm 安装 koa,pnpm会将包放到一个全局的store目录中( $XDG_STATE_HOME/pnpm), 项目node_modules通过hard link引入
展开项目node_modules,可以看到仅仅只有koa以及mime-types(因为包名命中了默认配置,所以被提升到顶层)被安装顶层,并且顶层包都是软链到 /node_modules/.pnpm/ ,也就说实际上所有包都被平铺到.pnpm目录中
进入到.pnpm目录中,由此就可以知道项目依赖实际上是放在 .pnpm/{包名@版本}/node_modules/, 然后通过hard link到全局的store目录中
所以pnpm安装速度快的原因在于,所有通过pnpm安装的依赖都在store目录中存放,重复安装的依赖都只是新建hard link,对比npm 重复下载、解压,自然就快很多了
Efficient
从上面的实验可以看到,pnpm对比npm重复安装可以省下大量的磁盘空间,对磁盘IO利用率非常高
Supports monorepos
关于monorepos不多做介绍,感兴趣的同学可以自己google下。
pnpm 构建 monorepos 主要还是由官方提供的一个 workspaces 协议支持,值得一提的就是Vue3也采用了pnpm支持monorepos
起步
按照官方指引,我们初始化一个monorepos项目,目录结构大概长这样
packages/utils/index.js
module.exports = {
say() {
return 'I am utils'
}
}
package.json
{
"name": "@test/utils",
"version": "1.0.0",
"description": "",
"main": "index.js",
"author": "jayzou",
"license": "MIT"
}
在app中引入utils,相关命令可自行查阅文档
pnpm i @test/utils --filter @test/app //只在app里面引入
packages/app/package.json
{
"name": "@test/app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@test/utils": "workspace:^1.0.0"
}
}
workspace实际上pnpm用来标记本地包,在打包构建的时候还原成标准包版本
{
"dependencies": {
"foo": "workspace:*",
"bar": "workspace:~",
"qar": "workspace:^",
"zoo": "workspace:^1.5.0"
}
}
//转换成
{
"dependencies": {
"foo": "1.5.0",
"bar": "~1.5.0",
"qar": "^1.5.0",
"zoo": "^1.5.0"
}
}
但是经笔者实验,替换workspace协议只对当前目录下的package.json生效,如果直接在根目录下执行 pnpm publish 的话,实际上子包并没有被替换,需要对所有子包都执行一遍publish
pnpm recursive publish
对比 lerna + yarn
lerna + yarn也是开源界实践 monorepos 非常火的一种方案了,无论是业界的文章、生态以及各种实践的项目都是非常丰富。
对于我个人来言,我非常看好pnpm的发展前景,但是如果从技术选型的角度上来看,我还是比较倾向于lerna,理由如下
功能更加全面
lerna从配置上来看比pnpm 相对复杂一些,但是配置的学习成本并不高,熟悉一段时间后很快也能上手,但是功能全面更适合一些复杂的业务场景,比如 semver 管理、发版方式等。pnpm 对这些支持非常弱,需要搭配第三方库或者自己实现
社区
lerna出来已经有一些年头了,社区积累了一堆经验总结文章,基本你遇到问题都能找到,而pnpm相对还是比较新,基本都只能依赖官方文档,出问题只能查官方文档或者自己看源码
Strict
要说这个特性,得先介绍下原生npm 是怎么安装依赖,比如我们在项目下执行 npm install koa,你会发现node_modules会出来非常多包,按照node模块依赖检索规则,所有node_modules目录下的一级模块都可以直接在代码中引入,这就导致了在代码中意外引入非预期的包
其实在早期npm2.x的版本中并没有"打平"这些包,而是独立一个koa包,但是由于各个包之间互相依赖导致npm包重复度极高且入口非常深,基于以上几点npm3.x决定将依赖包"打平",当然代价就是目前遇到的"幽灵依赖"的问题
总结
不得不说看完pnpm的设计还是让人眼前一亮的感觉,解决了很多npm、yarn本身因设计原因遗留下来的问题。但是相应的设计上也非常超前,比如生成的pnpm-lock.yaml跟官方package-lock.json差别比较大,二者无法兼容,还有就是社区热度上还不够,很多官方文档未普及到的场景还需要自己实现。最后期待作者能继续加油打磨pnpm上的能力以及社区的热度,个人还是非常期待的!