【pnpm】从了解到应用

包管理器简史

npm 3 之前

node_modules 结构是有序的

假设有如下的依赖项:

.
├── package-a
│   └── lodash
├── package-b
│   └── lodash
├── package-c
│   └── lodash
└── package-d
    └── lodash

node_modules 结构如下:

node_modules
├── package-a
│   └── node_modules
│       └── lodash
├── package-b
│   └── node_modules
│       └── lodash
├── package-c
│   └── node_modules
│       └── lodash
└── package-d
    └── node_modules
        └── lodash

上面结构有 2 个严重的问题:① 会创建过深的依赖树、② 会重复安装依赖包


npm 3+ & yarn

为了解决上述的两个问题,扁平化 node_modules

假设有如下的依赖项:

.
├── package-a
│   └── lodash
├── package-b
│   └── lodash
├── package-c
│   └── lodash
└── package-d
    └── lodash

在 npm 3+ 和 yarn 中,node_modules 结构变成:

node_modules
├── package-a
├── package-b
├── package-c
├── package-d
├── lodash

扁平化 node_modules 简化了依赖树的结构,也减少了 node_modules 的体积


但扁平化 node_modules 后仍存在一些问题:

① 仍可能会创建过深的依赖树、重复安装依赖包

假设存在依赖项:

.
├── package-a
│   └── lodash@4.0.0
├── package-b
│   └── lodash@4.0.0
├── package-c
│   └── lodash@3.0.0
└── package-d
    └── lodash@3.0.0

node_modules 结构变成:

node_modules
├── package-a
├── package-b
├── package-c
│   └── node_modules
│       └── lodash
├── package-d
│   └── node_modules
│       └── lodash
├── lodash

这里只有一个版本的依赖会被提升,具体哪个依赖被提升需要看哪个依赖先被下载

② 扁平化 node_modules 会导致 “幽灵依赖” 问题

假设有如下的依赖项:

.
├── package-a
├── package-b
├── package-c
└── package-d
    └── lodash

扁平化 node_modules 后,node_modules 的结构变成:

node_modules
├── package-a
├── package-b
├── package-c
├── package-d
├── lodash

因为使用的是扁平化的 node_modules 结构,使开发者有机会使用未在 package.json 中声明的依赖包

比方说,我下载 express,那 express 所依赖的包,都会下载到与 express 同一层级的目录下。假设,我现在要使用 express 所依赖的包中的 body-parser,我不需要再 npm i body-parser 便可直接使用。

这会出现一个问题,项目的 package.json 中没有设置 body-parser 的依赖。

如果某一天,express 升级了,不依赖 body-parser 了,但项目中还用到 body-parser,此时项目就会报错;或者说,某一天依赖的 body-parser 的版本升级了,但项目中用到的还是旧版本的 API,项目也会报错。



依赖安装原理

对于未被完全解决的问题:① 会创建过深的依赖树、② 会重复安装依赖包,以及扁平化 node_modules 导致的新问题 “幽灵依赖”,pnpm 使用 [软链接] + [硬链接] + [内容寻址存储] 解决


硬链接 & 软链接

在 Linux 的文件系统中,保存在磁盘分区中的文件都有一个编号,称为 [索引节点号] Inode Index

  • 硬链接与源文件指向同一个物理地址,具有相同的 Inode Index;硬连接的作用:允许一个文件拥有多个有效路径名,这样用户就可以给重要文件建立硬链接,以防 “误删”。就是说,只删除一个链接并不影响源文件和其它的链接,只有最后一个链接被删除后,文件的数据块才会被释放

  • **软链接(符号链接)**相当于一个快捷方式;在软链接中,文件实际上是一个文本文件,文件内容是另一个文件的位置信息


可以在 Virtual x86 上面模拟 Linux 命令行操作:

① 创建源文件及其软硬链接

root@superman:~/link_test# vi link
root@superman:~/link_test# cat link
superman
root@superman:~/link_test# ln link link-hard
root@superman:~/link_test# ln -s link link-soft
root@superman:~/link_test# ls -li
total 8
1443783 -rw-r--r-- 2 root root 9 Aug 13 12:01 link
1443783 -rw-r--r-- 2 root root 9 Aug 13 12:01 link-hard
1443782 lrwxrwxrwx 1 root root 4 Aug 13 12:02 link-soft -> link

② 通过软硬连接访问源文件

root@superman:~/link_test# cat link-hard
superman
root@superman:~/link_test# cat link-soft
superman

③ 修改源文件,查看软硬链接

root@superman:~/link_test# vi link
root@superman:~/link_test# cat link
superman monster
root@superman:~/link_test# cat link-hard
superman monster
root@superman:~/link_test# cat link-soft
superman monster

④ 删除源文件,查看软硬链接

root@superman:~/link_test# rm link
root@superman:~/link_test# ls -li
total 4
1443783 -rw-r--r-- 1 root root 17 Aug 13 12:02 link-hard
1443782 lrwxrwxrwx 1 root root  4 Aug 13 12:02 link-soft -> link
root@superman:~/link_test# cat link-hard
superman monster
root@superman:~/link_test# cat link-soft
cat: link-soft: No such file or directory

内容寻址存储

内容寻址存储(Content Addressable Storage,CAS)是一种存储和检索数据的方法。

CAS 基于数据的内容计算出唯一的哈希值,并将其用作数据的索引。因此,相同内容的数据块将始终具有相同的哈希值,可以通过哈希值快速检索数据。


依赖安装

假设存在依赖项:

.
├── package-a
│   └── lodash@4.0.0
├── package-b
│   └── lodash@4.0.0
├── package-c
│   └── lodash@3.0.0
└── package-d
    └── lodash@3.0.0

在首次安装依赖包时,pnpm 会将依赖包下载到一个公共目录 store 中。
在下一次安装依赖包时,pnpm 就会从公共目录 store 中找到已安装的依赖包,并创建硬链接到项目中。

使用 pnpm 下载,所有的依赖包会平铺在 node_module/.pnpm 目录下;
因为使用了 CAS 技术,所以不同版本号的依赖包可以区别开来。

然后,pnpm 会在 node_modules 目录下创建一级依赖包的软链接

node_modules
├── .pnpm
│   └── package-a  (hard-link to store)
│   └── package-b  (hard-link to store)
│   └── package-c  (hard-link to store)
│   └── package-d  (hard-link to store)
│   └── lodash@3.0.0  (hard-link to store)
│   └── lodash@4.0.0  (hard-link to store)
├── package-a  (soft-link to ./.pnpm/package-a)
├── package-b  (soft-link to ./.pnpm/package-b)
├── package-c  (soft-link to ./.pnpm/package-c)
├── package-d  (soft-link to ./.pnpm/package-d)

注意,此时 .pnpm 的结构不是扁平化的:

node_modules
├── .pnpm
│   └── package-a
│   │   └── node_modules
│   │       └── lodash  (soft-link to ../../lodash@4.0.0)
│   └── package-b
│   │   └── node_modules
│   │       └── lodash  (soft-link to ../../lodash@4.0.0)
│   └── package-c
│   │   └── node_modules
│   │       └── lodash  (soft-link to ../../lodash@3.0.0)
│   └── package-d
│   │   └── node_modules
│   │       └── lodash  (soft-link to ../../lodash@3.0.0)
│   └── lodash@3.0.0
│   └── lodash@4.0.0
├── package-a
├── package-b
├── package-c
├── package-d



管理 Node 版本

pnpm 与其他 npm 包管理工具不同的是,pnpm 可以管理 Node 版本。

注意:如需使用 pnpm 管理 Node 版本,需要删除任何由其他工具安装的 Node,然后使用 pnpm - Installation 上提供的独立脚本来安装 pnpm


执行脚本

  1. 安装并使用 LTS 版本的 Node:pnpm env use -g lts

    安装并使用 v16 版本的 Node:pnpm env use -g 16

  2. 移除指定版本的 Node:pnpm env rm --global 14.0.0

  3. 输出本地安装的版本:pnpm env ls

    输出远程可用的版本:pnpm env ls --remote

    输出远程可用的 Node 16 版本:pnpm env ls --remote 16


执行 pnpm env use -g xxx 时,可能会抛出如下错误:

  1. ERROR: Unable to find the global bin directory
    要先执行 pnpm setuppnpm setup 会:① 为 pnpm CLI 创建一个主目录、② 通过更新 shell 配置文件将 pnpm 主目录添加到 PATH、③ 将 pnpm 可执行文件复制到 pnpm 主目录

  2. PERM: operation not permitted, symlink XXX
    需要以管理员身份执行


配置 .npmrc

在配置文件 .npmrc 中配置 use-node-version 可指定项目运行时应用的 Node 版本,支持 semver 版本设置规范。设置后, pnpm 将自动安装指定版本的 Node 并将其用于执行 pnpm run / pnpm node 命令

use-node-version=^16.x



配置项目

项目迁移

pnpm import 可基于其它类型的 lock 文件生成 pnpm-lock.yaml 文件,目前支持:

  1. package-lock.json

  2. npm-shrinkwrap.json

  3. yarn.lock


只允许 pnpm

当您在项目中使用 pnpm 时,如果不希望被其他人意外运行 npm iyarn,可在 package.json 中配置 pnpm 的生命周期脚本 preinstall

{
    "scripts": {
        "preinstall": "npx only-allow pnpm"
    }
}

现在,只要有人运行 npm iyarn,就会发生错误并且不会继续安装。

脚本 preinstall 仅在执行 pnpm install 时运行,会在安装任何依赖项之前运行


扁平化处理

使依赖及其子依赖都平铺到 node_modules 目录下,有如下 3 种方法:

① 配置 .npmrc:

shamefully-hoist=true

② 执行 pnpm install --shamefully-hoist

③ 配置 package.json:

{
    // ...
    "pnpm": {
        "shamefully-hoist": true
    }
}



基础语法

  1. 安装 pnpm:npm i pnpm -g

  2. 查看版本:pnpm -v


常用命令

  1. pnpm i

  2. pnpm i xxx / pnpm add xxx

  3. pnpm rm xxx / pnpm un xxx

  4. pnpm run xxx / pnpm xxx


其他命令

  1. pnpm init

  2. pnpm ls

  3. pnpm update / pnpm up

  4. pnpm prune:移除未被使用的依赖包

    pnpm store prune:移除 pnpm 的全局存储中未被使用的依赖包


配置 pnpm

  1. pnpm config ls:查看 pnpm 配置

  2. pnpm config get registry:查看镜像

    pnpm config set registry xxx:修改镜像

  3. pnpm config get store-dir:查看 pnpm 的全局存储目录

    pnpm config set store-dir ~/.my-pnpm-store:修改 pnpm 的全局存储目录

如果未配置 pnpm 的全局存储目录,则 pnpm 会在同一硬盘上自动创建一个存储。

注意:项目应该与 store 在同一个磁盘上,否则 pnpm 会将依赖包复制到项目中,而不是使用硬链接(这是因为硬链接不能跨磁盘使用)。项目仍然会保持 pnpm 的优势,但是每个驱动器可能有多余的包。

在安装依赖项时,pnpm 与 npm 使用相同的配置。如果你为 npm 配置了一个私有的软件包注册表, pnpm 也能够获得授权请求,并且无需任何额外的配置。


错误处理

pnpm: 无法加载文件 C:\XXX\pnpm.ps1,因为在此系统上禁止运行脚本。

  1. win + s 在系统中搜索框输入 “PowerShell” 右击 “管理员身份运行”

  2. 输入 “set-ExecutionPolicy RemoteSigned” 回车,根据提示输入 A,回车

  3. 再次 pnpm -v 执行成功

不只是 pnpm 命令,包括 cnpm、yarn 等这些命令,如果执行时报这样的错误,都可以通过此方法解决。

set-ExecutionPolicy RemoteSigned 是一个 PowerShell 命令,它用于设置 PowerShell 的执行策略。执行策略是一种安全机制,它决定了你是否可以运行 PowerShell 脚本,以及这些脚本是否需要被数字签名。set-ExecutionPolicy RemoteSigned 的意思是,你可以运行你自己写的或者从本地计算机上获取的未签名的脚本,但是如果是你从网络上获取的脚本,它们必须被可信任的发布者签名,否则无法运行。这样可以防止你运行一些恶意的或者不安全的脚本。


  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JS.Huang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值