password
Created
Feb 19, 2023 04:36 PM
type
Post
status
Published
date
May 8, 2022
slug
summary
包管理工具的提速与演进
tags
Npm
Pnpm
Yarn
category
工程化
icon
前言: 包管理工具的演进
包管理工具的不同阶段
Gen1:
npm的时代: 依赖为树形结构, 有时候往往拉了一个包, 因为这个包依赖了别的包而导致要拉无数个包
Gen2:
yarn的依赖革命:
- 分析依赖树, 将依赖打平来安装, 避免重复依赖项(如果是同一包的多版本, 会将其他版本安装到包下的node_modules中)
- 安装过的依赖会被全局缓存, 下次安装的时候会将缓存的依赖复制到项目中, 节省网络IO的时间
- lockfile 锁定版本, 节省获取远程版本的网络IO开支
- 并行处理
yarn的流程: resolution > fetching > linking
Gen3:
pnpm的优化
- 依然使用了缓存的机制, 但不同的是, 全局缓存了包内容, 但项目中是全局缓存包的链接, 这样相当于使用包变成了使用这个全局包的链接, 减少了磁盘IO的成本
- 解决了yarn打平依赖造成的Ghost Dependency的问题, 使用超链接的嵌套结构
Gen4:
tnpm
- 服务端生成依赖树, 降低网络io成本
- 使用tar来存储文件, 因为之前的包管理工具都是对文件内容通过tar压缩传输之后, 再在本地解压成文件. 使用unix底层的FUSE文件系统, 将文件的管理权交给了脚本, 也就允许了文件只需要通过tar来存储, 需要的时候解压即可
- 使用类pnpm的软链机制, 全局缓存文件以减小文件存储成本
pnpm的存储机制
在使用pnpm的项目中, 我们可以看到`node_modules`中有如下的结构
- .bin - .pnpm | - PACKAGR_NAME@VERSION | - lock.yaml - .modules.yarml - PROJECT_NAME
pnpm使用了平铺的方式在.pnpm中携带版本号存储所有的包
对应的在{home dir}>/.pnpm-store/v3中, 会进行全局包的存储
我们可以看到, 存储的结构中都是两个字符开头的目录, 目录中是一串串hash为名的文件, 是因为采用了CAS的存储方式, 主要的好处是: 1. 方便进行归档和保存, 节省空间占用 2. 因为是使用的内容的hash来进行存储的, 所以文件一旦被篡改就能够被获知, 因为这时候hash已经变了
在每次安装完之后, 可以看到两个关键字
Content-addressable store: CAS 内容寻址存储, 是一种存储信息的方式, 根据内容而不是位置进行检索信息的存储方式
Virtual store 虚拟存储: 指向存储的链接的目录,所有直接和间接依赖项都链接 到此目录中,项目当中的.pnpm目录
安装大提速: hard link + symbolic link
通过
hard link
, 用户可以通过不同的路径引用方式去找到某个文件,需要注意的是一般用户权限下只能硬链接到文件,不能用于目录我们可以再将目光回到node_modules目录中, 想一想, 对于依赖包, 为什么会有.pnpm文件, 并且这个文件会将所有包放在一起呢?
官方有一个图可以很好的解释这一点
.pnpm文件中的存储的都是store中的依赖包的硬链接, 我们安装的外层依赖会使用软连接的方式链接到.pnpm中, 而其中, 如果有包的相互依赖关系时, 会打平处理
同时需要注意的是.pnpm中的node_modules文件, 是存储了其依赖关系及本身的, 其本身, 才会进行硬链接到store中
关于为什么使用hard link
+symbolic link
而不是直接使用symbolic link
1. symlink 即软连接的方式可能会在 windows 存在一些兼容的问题,但是针对这个问题,pnpm 也提供了对应的解决方案 2. 在 win 系统上使用一个叫做 junctions 的特性来替代软连接,这个方案在 win 上的兼容性要好于 symlink 3. 实际上存在 store 目录里面的依赖也是可以通过软连接去找到的,nodejs 本身有提供一个叫做--preserve-symlinks
的参数来支持 symlink,但实际上这个参数实际上对于 symlink 的支持并不好导致作者放弃了该方案从而采用 hard links 的方式 参考:Improving support for symlinks in the module systemUpdated Oct 13, 2017
pnpm目前存在的问题
- jest单测运行失败
jest低版本不支持软连接, 要升级(25.2.0+)
- node-gyp rebuild failures
pnpm低版本bug, 升级pnpm(6.23.1+)
- 同一个版本的包有两份副本
peerDependencies在pnpm上的实现的问题
问题解决: 添加 .pnpmfile.cjs 文件,忽略 peerDependencies,使其对 peer 的处理与 yarn 保持一致
// .pnpmfile.cjs function readPackage(pkg, context) { if (pkg.name && pkg.peerDependencies) { // https://pnpm.io/zh/how-peers-are-resolved pkg.peerDependencies = {} } return pkg } module.exports = { hooks: { readPackage, }, }