了解 pnpm 的优势,然后将已有项目的 yarn 换成 pnpm

之前一直接触到 pnpm,但没有应用到业务项目,最近的业务项目安装和编译时间太长了,所以尝试性的换成 pnpm.

pnpm 官网介绍了 pnpm 的优点:

  • 中心化存储,避免同一个文件的重复存储,节省磁盘空间和安装时间。
  • 创建非扁平的 node_modules,解决幽灵包的问题。

pnpm 优点 1:中心化存储

传统工具(npm/yarn)的局限性:

  • 重复安装问题:同一个项目安装一个包的不同版本时,会复制多份;在不同项目中安装相同依赖时,会完整复制整个依赖包到项目目录。
  • 安装速度慢:网络传输和磁盘写入时间随依赖数量线性增长(上面的问题导致的这个问题)

同一项目内多版本依赖:

假设在一个项目中,有两个包:

  • package-a 依赖 dayjs@1.11.0
  • package-b 依赖 dayjs@1.10.0

npm 和 yarn 的存储结构如下:

shell 复制代码
# 项目结构
project/
├── node_modules/
│   ├── package-a/
│   │   └── node_modules/
│   │       └── dayjs@1.11.0/
│   ├── package-b/
│   │   └── node_modules/
│   │       └── dayjs@1.10.0/
│   └── dayjs@1.11.0/  #(比较新的版本提取到根目录)

存储空间如下:

graph TB subgraph "项目结构 (npm、yarn)" A[project/] A --> B[node_modules/] B --> C[package-a/] B --> D[package-b/] B --> E[dayjs1.11.0/] C --> F[node_modules/] F --> G[dayjs1.11.0/] D --> H[node_modules/] H --> I[dayjs1.10.0/] end subgraph "存储空间" J[package-a/dayjs1.11.0: 10MB] K[package-b/dayjs1.10.0: 10MB] L[根目录/dayjs1.11.0: 10MB] M[总计: 30MB] end G --> J I --> K E --> L style M fill:#f99,stroke:#333,stroke-width:2px

pnpm 在同一项目内多版本依赖:

pnpm 会存储在全局存储中,而不是项目中,所以不会占用项目空间。

同样,dayjs@1.11.0 和 dayjs@1.10.0 的文件也会存储在全局存储中,但是 pnpm 会对每个文件进行哈希计算,如果文件内容没有变化,则不会重复存储,只会存储差异部分。

也就是如果升级包的版本,但只有 1 个文件修改了,那么只会存储这个文件。而在项目中,pnpm 会通过硬链接的方式,将全局存储中的文件链接到项目中,所以相同文件不会占用额外磁盘空间。

bash 复制代码
# 全局存储
~/.pnpm-store/v10/files/
├── 00/abc123...  # dayjs@1.11.0 的 package.json
├── 00/def456...  # dayjs@1.11.0 的 index.js
├── 00/ghi789...  # dayjs@1.10.0 的 package.json
└── 00/jkl012...  # dayjs@1.10.0 的 index.js

# 项目结构
project/
├── node_modules/
│   ├── package-a -> .pnpm/package-a@1.0.0/node_modules/package-a
│   └── package-b -> .pnpm/package-b@1.0.0/node_modules/package-b
└── .pnpm/
    ├── package-a@1.0.0/
    │   └── node_modules/
    │       ├── package-a/
    │       └── dayjs -> ../../dayjs@1.11.0/node_modules/dayjs
    ├── package-b@1.0.0/
    │   └── node_modules/
    │       ├── package-b/
    │       └── dayjs -> ../../dayjs@1.10.0/node_modules/dayjs
    ├── dayjs@1.11.0/
    │   └── node_modules/
    │       └── dayjs/
    │           ├── package.json -> ~/.pnpm-store/v10/files/00/abc123...
    │           └── index.js -> ~/.pnpm-store/v10/files/00/def456...
    └── dayjs@1.10.0/
        └── node_modules/
            └── dayjs/
                ├── package.json -> ~/.pnpm-store/v10/files/00/ghi789...
                └── index.js -> ~/.pnpm-store/v10/files/00/jkl012...

存储空间如下:

graph TB subgraph "项目结构 (pnpm)" A[project/] A --> B[node_modules/] A --> C[.pnpm/] B --> D[package-a -> .pnpm/package-a1.0.0/] B --> E[package-b -> .pnpm/package-b1.0.0/] C --> F[package-a1.0.0/] C --> G[package-b1.0.0/] C --> H[dayjs1.11.0/] C --> I[dayjs1.10.0/] F --> J[node_modules/] J --> K[package-a/] J --> L[dayjs -> ../../dayjs@1.11.0/] G --> M[node_modules/] M --> N[package-b/] M --> O[dayjs -> ../../dayjs@1.10.0/] H --> P[node_modules/dayjs/] I --> Q[node_modules/dayjs/] end subgraph "全局存储" R[~/.pnpm-store/v10/files/00/abc123...] S[~/.pnpm-store/v10/files/00/def456...] T[~/.pnpm-store/v10/files/00/ghi789...] U[~/.pnpm-store/v10/files/00/jkl012...] end P --> R P --> S Q --> T Q --> U style R fill:#9f9,stroke:#333,stroke-width:2px style S fill:#9f9,stroke:#333,stroke-width:2px style T fill:#9f9,stroke:#333,stroke-width:2px style U fill:#9f9,stroke:#333,stroke-width:2px

不同项目使用同一个依赖:

假设有两个项目都依赖了 同版本的 dayjs,那么 pnpm 会存储在全局存储中只存储一份,然后通过硬链接的方式,将全局存储中的文件链接到项目中,所以相同文件不会占用额外磁盘空间。

也就是同一个项目可以共享不同版本的依赖,而不会占用额外磁盘空间。不同项目可以共享同一个依赖,而不会占用额外磁盘空间。

因为 pnpm 本质就是基于内容寻址存储,只要文件相同,就不会重复存储,不管是同一个项目还是不同项目。

pnpm 优点 2:创建非扁平的 node_modules

幽灵包问题:

假设在一个项目中,有个包,其依赖如下:

  • package-a 依赖 dayjs@1.11.0

  • npm 和 yarn 中,在项目里可以通过import dayjs from 'dayjs'来使用 dayjs。

  • pnpm 中,在项目里不可以 通过import dayjs from 'dayjs'来使用 dayjs。

因为在 npm 和 yarn 中,依赖是扁平的,所以 dayjs 被存储到了 node_modules 的根目录。而 pnpm 中,依赖是非扁平的,所以 dayjs 被存储到了 package-a 的 node_modules 的根目录中。

pnpm 的原理:

  • 内容寻址存储:依赖文件通过哈希值存储在全局唯一位置,每次安装都只会存储差异部分,也就是如果升级包的版本,但只有 1 个文件修改了,那么只会存储这个文件。
  • 硬链接技术:项目中的依赖文件通过硬链接指向全局存储,所以相同文件不会占用额外磁盘空间。
  • 跨项目共享依赖:相同版本的依赖文件可以被多个项目共享,避免了重复安装。

已有项目 yarn 换成 pnpm

已有项目 yarn 换成 pnpm,需要以下步骤:

  1. 安装 pnpm
bash 复制代码
npm install -g pnpm
  1. 删除 node_modules 和 重命名 yarn.lock 为 yarn.lock.bak
bash 复制代码
rm -rf node_modules
mv yarn.lock yarn.lock.bak
  1. 使用 pnpm 安装依赖 ,并生成 pnpm-lock.yaml
bash 复制代码
pnpm install
  1. 部署脚本更新

如果项目有部署脚本,需要更新部署脚本,将 yarn 换成 pnpm。

bash 复制代码
nvm use 18
pnpm config set registry https://registry.npmmirror.com/
pnpm config set @df:registry http://mir.test.df.cn/npm/
pnpm config set store-dir ~/.pnpm-store
pnpm config set cache-dir ~/.pnpm-cache
pnpm config set prefer-offline true

pnpm install
pnpm run build

可能遇到的问题

之前的 yarn、npm 安装,可能有幽灵包的情况,如果启动项目,遇到模块找不到的情况,可以尝试以下步骤:

bash 复制代码
mv yarn.lock.bak yarn.lock
yarn list xx包

看下版本是啥,比如

shell 复制代码
$ yarn list ahooks
yarn list v1.22.19

warning Filtering by arguments is deprecated. Please use the pattern option instead.
├─ @xdf/b-table@1.0.4
│  └─ ahooks@2.10.14
├─ @xdf/channel-selector-v2@3.3.1
│  └─ ahooks@2.10.14
├─ @xdf/cus-pro-table@1.5.15
│  └─ ahooks@2.10.14
└─ form-render@2.5.2
   └─ ahooks@3.8.4
✨  Done in 1.01s.

可以看到 ahooks 的版本是 2.10.14,那么可以安装这个版本,顺手把 yarn.lock 还原回去

bash 复制代码
pnpm install ahooks@2.10.14; mv yarn.lock yarn.lock.bak;

如果项目有 test 功能,运行 test 功能,没有的话,可能需要自己点点页面,看看是否正常。当然最好是借此机会,写写测试用例,覆盖下代码,(* ̄︶ ̄)

相关推荐
海在掘金611273 小时前
从"鬼知道这对象有啥"到"一目了然" - TS接口的实战魔力
前端
spionbo3 小时前
Vue 模拟键盘组件封装方法与使用技巧详解
前端
顾青3 小时前
微信小程序 VisionKit 实战(二):静态图片人脸检测与人像区域提取
前端·微信小程序
hmfy3 小时前
那些前端老鸟才知道的秘密
前端
野葳蕤3 小时前
react总览
前端
不一样的少年_3 小时前
她说想要浪漫,我把浏览器鼠标换成了柴犬,点一下就有烟花(附源码)
前端·javascript·浏览器
顾青3 小时前
微信小程序实现身份证识别与裁剪(基于 VisionKit)
前端·微信小程序
星链引擎3 小时前
技术深度聚焦版(侧重技术原理与代码细节)
前端
呵阿咯咯3 小时前
ueditor富文本编辑器相关问题
前端