了解 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 功能,没有的话,可能需要自己点点页面,看看是否正常。当然最好是借此机会,写写测试用例,覆盖下代码,(* ̄︶ ̄)

相关推荐
木易 士心5 小时前
Vue 3 Props 响应式深度解析:从原理到最佳实践
前端·javascript·vue.js
海鸥两三8 小时前
uniapp 小程序引入 uview plus 框架,获得精美的UI框架
前端·vue.js·ui·小程序·uni-app
lightgis9 小时前
16openlayers加载COG(云优化Geotiff)
前端·javascript·html·html5
小飞大王6669 小时前
TypeScript核心类型系统完全指南
前端·javascript·typescript
你的人类朋友11 小时前
✍️记录自己的git分支管理实践
前端·git·后端
合作小小程序员小小店11 小时前
web网页开发,在线考勤管理系统,基于Idea,html,css,vue,java,springboot,mysql
java·前端·vue.js·后端·intellij-idea·springboot
防火墙在线11 小时前
前后端通信加解密(Web Crypto API )
前端·vue.js·网络协议·node.js·express
Jacky-00812 小时前
Node + vite + React 创建项目
前端·react.js·前端框架
CoderYanger12 小时前
前端基础——CSS练习项目:百度热榜实现
开发语言·前端·css·百度·html·1024程序员节
i_am_a_div_日积月累_12 小时前
10个css更新
前端·css