1. 优势
1.1 节省磁盘空间
下面是 pnpm 官网的介绍
使用 npm 时,如果你有 100 个项目,并且所有项目都有一个相同的依赖包,那么, 你在硬盘上就需要保存 100 份该相同依赖包的副本
如果是使用 pnpm,依赖包将被存放在一个统一的位置。每一个文件都单独进行保存到仓库
如果发布了一个新版本,并且新版本中只有一个文件有修改,则 pnpm update
只需要添加一个新文件到存储中,而不会因为一个文件的修改而保存依赖包的所有文件
1.2 更好的 node_modules 管理
仅将项目的直接依赖项添加到 node_modules
的根目录下 , 下面是使用 pnpm 安装 vue
以后 node_modules
显示的内容,可以看见node_modules
下面只有vue
,而 vue
依赖的第三方包都在 .pnpm
下面,这样只有真正处于依赖关系的包才可以访问
考虑这样一个场景:
假如第三方包安装了axios,如果这个axios在node_modules里面,那么开发者可以直接通过import引用。那假如说第三方包在某次升级中,把axios给去掉了。那么在使用开发的时候就将出现问题。
关于node_modules更详细的内容会在后面进行描述
2. 安装配置
要求:node > v16.14
2.1 安装
npm install pnpm -g
2.2 .npmrc
-
pnpm
的配置文件同npm
一样,都是.npmrc
,也就是说对npm
的设置默认对pnpm
生效 -
要像
npm
那样组织node\modules
,需要在.npmrc
里面设置hoist=true
-
锁文件
pnpm-lock.yaml
2.2.1 pnpm 关键的配置
- store-dir 仓库地址
- Default:
- If the $PNPM_HOME env variable is set, then $PNPM_HOME/store
- If the $XDG_DATA_HOME env variable is set, then $XDG_DATA_HOME/pnpm/store
- On Windows: ~/AppData/Local/pnpm/store
- On macOS: ~/Library/pnpm/store
- On Linux: ~/.local/share/pnpm/store
相关命令
pnpm store path 直接查询全局存储路径
pnpm config get store-dir 查询的是配置文件中的存储路径设置
pnpm config set store-dir /path/pnpm/store -g 设置仓库存储路径
2. global-dir 全局安装路径
- Default:
- If the $XDG_DATA_HOME env variable is set, then $XDG_DATA_HOME/pnpm/global
- On Windows: ~/AppData/Local/pnpm/global
- On macOS: ~/Library/pnpm/global
- On Linux: ~/.local/share/pnpm/global
相关命令
pnpm config get global-dir
pnpm config set global-dir /path/pnpm/global -g
当使用 npm add A -g
安装全局包,安装的内容也会在 store 里面
3. global-bin-dir 全局可执行文件的路径
- Default:
- If the $XDG_DATA_HOME env variable is set, then $XDG_DATA_HOME/pnpm
- On Windows: ~/AppData/Local/pnpm
- On macOS: ~/Library/pnpm
- On Linux: ~/.local/share/pnpm
相关命令
pnpm config get global-bin-dir
pnpm config set global-bin-dir /path/pnpm -g
设置完毕以后,使用类似于 pnpm add eslint -g
时,对应的可执行文件,会安装到这个目录里面
2.4 pnpm add xxx -g
如果执行 pnpm add xxx -g
出现下面的报错
- 首先我们可以执行
pnpm setup
,执行完成后会在~
下面生成.bashrc
,内容如下
# pnpm
export PNPM_HOME="/Users/tangfeng/Library/pnpm"
case ":$PATH:" in
*":$PNPM_HOME:"*) ;;
*) export PATH="$PNPM_HOME:$PATH" ;;
esac
# pnpm end
.bash_profile 会调用 .bashrc 里面的内容。也就是会把 bin 文件的默认地址(/Users/tangfeng/Library/pnpm
)添加到环境变量 PATH 中。
- 如果按照上面的操作还有问题,那么就需要我们手动设置
global-dir
和global-bin-dir
,然后配置环境变量
# 首先设置
pnpm config set global-dir ~/pnpm/global -g
pnpm config set global-bin-dir ~/pnpm -g
以 macos 为例
# ~/.bash_profile
export PATH=~/.npm-global/bin:~/pnpm:$PATH
~/pnpm
就是我们设置的 global-bin-dir
,PATH 以 : 作为分割符,最后的 $PATH
代表的是系统默认设置的一些路径
注意: 设置完成以后执行 source ~/.bash_profile
才能生效
理解: 当我们执行类似于eslint xx
的命令,会到环境变量对应的路径下找eslint
的可执行文件
3. 命令
npm | yarn | pnpm | 描述 |
---|---|---|---|
npm install | yarn install | pnpm install | 安装所有包 |
npm i pkg | yarn add | pnpm add pkg | 安装特定包 |
npm run cmd | yarn cmd | pnpm cmd/pnpm run cmd | 执行脚本 |
npm update | yarn upgrade | pnpm update/up/upgrade | 升级包 |
npm uninstall | yarn remove | pnpm remove/rm/uninstall/un | 移除包 |
pnpm install
pnpm
安装的逻辑与npm
并不完全相同,假如package.json
的内容如下
"dependencies": {
"axios-a": "^1.0.2"
}
npm install
会默认安装 1.x.x
的最新版本,此时对应的就是 1.0.3
,而 pnpm
会默认安装当前版本,也就是 1.0.2
-
pnpm add
- 默认安装在
dependencies
,-D
安装在devDependencies
-g
全局pnpm add sax@next
,pnpm add sax@3.0.0
安装 sax 指定版本
- 默认安装在
-
pnpm update
- 当我们执行
pnpm update axios-a
时,更新逻辑符合^、~
的意思 package.json
里面也会更新pnpm up foo@2
更新到foo2.x的最新版本pnpm up "@babel/*"
更新所有 @babel 相关的包pnpm update !webpack
更新除 webpack 以外的所有包
- 当我们执行
-
pnpm config
# 配置相关设置 npm/yarn/pnpm config set key value npm/yarn/pnpm config get key npm/yarn/pnpm config delete key npm/yarn/pnpm config ls
-
npm/yarn/pnpm init
项目初始化 -
npm/yarn/pnpm publish
发布 -
pnpm exec
有点类似于 npx 指令,从node_modules/.bin
找到可执行文件,然后执行(区别是pnpm exec
并不会尝试下载安装) -
pnpm create
通过模版创建新项目pnpm/npm/yarn create reat-app my-app npx create-react-app my-app
4. 深入理解 pnpm 的包管理
4.1 node_modules
- 当我们安装 axios-a 以后,node_modules 下面的 axios-a 只是一个软链接,
它链接的位置是 .pnpm/axios-a@1.0.3/node_modules/axios-a
,.pnpm下面的包内容都包含了版本号
用一个 foo 引入了bar、bar引入了qar 的例子就是
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ ├── bar -> <store>/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar
├── foo@1.0.0
│ └── node_modules
│ ├── foo -> <store>/foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
└── qar@2.0.0
└── node_modules
└── qar -> <store>/qar
这样每一个包下面的 node_modules 就只会包含自己安装的那些包。解析模块时,node会忽略符号链接,直接去解析真实的位置
4.2 软链接、硬链接
- 索引结点,是一种文件目录瘦身策略。由于检索文件时只需用到文件名,因此可以将除了文件名以外的其他信息放到索引节点中。而目录项只需要包含文件名、索引节点指针。
我们访问文件时,可以通过索引结点指针找到索引结点的位置,再从中找到文件的物理地址。
4.2.1 软链接(符号链接)
window下软链接与快捷方式的区别
- 快捷方式自己就是一个文件,它的大小比较固定,里面包含了指向真实文件的信息;
- 查看软链接大小的时候,软链接的大小会被计算为指向的文件或目录的大小
4.2.2 硬链接
- 在
macos
里面,当 A、B两个项目同时安装 axios-a,然后在 A 里面的 node_modules 里面对 axios-a 进行修改,那么B里面的内容也将发生修改,因为它们的最终都指向的是相同的路径。也就是说(A/node_modules/.pnpm/axios-a@1.0.3/node_modules/axios-a
与B/node_modules/.pnpm/axios-a@1.0.3/node_modules/axios-a
与store
里面的内容是同一个)
在A
的 node_modules/axios-a/index.js
里面添加了1,打开B
里面相同的文件,会发现同样发生修改。
- 在
windows
里面,当修改A
里面的axios-a
的内容,并不会导致B
里面的axios-a
内容的修改,具体的表现参看 4.4
4.2.3 区分软、硬链接
软链接通常都会有一个箭头符号
windows 电脑里面可以通过vscode进行查看
4.3 store
- 根据
pnpm store path
得到的路径结果删除里面的内容,删除pnpm-lock.yaml
,然后执行pnpm add axios-a
,可以看到我们的 store 里面有3个文件。
而我们的 aixos-a 里面共有2个文件
分别在vscode里面打开 store 里面的内容可以发现
8fxxxx --> package.json
61xxxx --> index.json
另外我们打开 4bxxxx
,可以看到它的内容如下
其中 integrity
就是这个文件通过hash
算法得到的hash
值。当我们使用 pnpm add pkg
安装包的时候,会对文件执行 hash
算法,并与integrity
进行比较,如果不相同,说明发生了改变,pnpm 将重新拉取最新的npm
包,从而保证每次 pnpm install
链接的包都是最新的。
4.4 修改node_modules的表现
场景:当我们要调试第三方包的时候,通常会对第三方进行修改
4.4.1 windows
当我们修改 node_modules/axios-a
里面的内容以后,如果要获取最新的包的内容,仅仅是 pnpm uninstall axios-a
是不够的,因为 .pnpm/axios-a
还在,此时如果直接执行 pnpm add axios-a
,会直接从 .pnpm/axios-a
里面进行链接,此时 axios-a
对应的内容还是修改后的内容。
直接使用 yarn install
也是无效的,
所以有效的解决方案是直接删除 node_modules
,然后重新执行 pnpm install
5. 其他注意事项
5.1 对项目中某些插件的影响
- 公司项目使用的引入排序的插件是
@trivago/prettier-plugin-sort-imports
,当使用pnpm
的时候该插件会失效,解决方法
// .prettierrc.js
module.exports = {
// ...
plugins: ['./node_modules/@trivago/prettier-plugin-sort-imports'],
}