• 作者:老汪软件技巧
  • 发表时间:2024-12-14 00:12
  • 浏览量:

pnpm是一款安装速度快且能够有效节省磁盘空间的包管理器工具,其基于符号链接的node_modules结构一方面大大提高安装的速度,另一方面也解决了传统npm或者yarn上存在的幽灵依赖和npm包分身的问题。另外,pnpm workspace的特性在monorepo工程化方面也有着不错的表现。

这篇文章记录pnpm学习实践过程的收获,有问题欢迎指正。

硬连接与软连接的定义与区别

软连接(也称作符号链接——symbolic link,symlinkorsoft link),是一类特殊的文件,其包含有一条以绝对路径或者相对路径的形式指向其它文件或者目录的引用。

一个符号链接文件仅包含有一个文本字符串,其被操作系统解释为一条指向另一个文件或者目录的路径。它是一个独立文件,其存在并不依赖于目标文件。如果删除一个符号链接,它指向的目标文件不受影响。如果目标文件被移动、重命名或者删除,任何指向它的符号链接仍然存在,但是它们将会指向一个不复存在的文件。这种情况被有时被称为被遗弃。

硬连接, 指通过索引节点来进行连接。在Linux的文件系统中,保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号(Inode Index)。在Linux中,多个文件名指向同一索引节点是存在的。一般这种连接就是硬连接。硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。

因为对应该目录的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。

基于符号连接的node_modules结构

pnpm 的node_modules布局使用符号链接来创建依赖项的嵌套结构。

image.png

pnpm的node_modules结构中,通常情况下只会保留package.json中声明的依赖包,并且这些依赖包会以软连接(vscode中文件名右侧的箭头表示即代表软连接文件)的方式直接放在node_modules中

这些软连接文件会指向node_module/.pnpm文件夹下的相应文件,这里面的文件是node_modules中的唯一的“真实”文件。

image.png

node_modules/.pnpm中的文件是以扁平化的方式组织的,这里面的文件都是以硬连接的方式链接到pnpm的全局store中,pnpm每次install新的包时,都会先从全局store中检查是否存在,若存在,则直接建立硬连接,大大减少了依赖安装的时间。

image.png

如上图所示,node_modules/.pnpm中依赖以及其依赖的依赖都硬连接到同一个node_modules文件中,这是必要的:

这里@eslint/core依赖的@types/json-sechma包也是通过软连接的方式扁平到.pnpm下

image.png

pnpm这样的node_modules设计与 Node 的模块解析算法完全兼容!解析模块时,Node 会忽略符号链接,而是解析到符号链接所指向的实际位置,由于实际位置的包也处于一个xxx/node_modules/xxx结构中,所以其依赖也可以被正确的解析。

这里放一张官方给的原理图,可以配合理解

image.png

幽灵依赖与npm包分身问题

在npm2版本之前,node_modules的结构是一个标准的嵌套树形结构,由于软件包可以依赖其他的包,这样的结构就导致大量公共的依赖包被重复安装到node_moudles中,所以在npm3之后,安装算法改成了将树扁平化,即将公共的依赖提升到根node_modules,这种提升就带来了幽灵依赖问题,也叫做幻影依赖。

幽灵依赖,指在项目中使用那些不被pakcage.json管理的依赖,由于npm的扁平化算法将部分公共依赖提升到了根node_modules,导致这些公共依赖可以被node的模块解析算法解析到,所以运行时不会出现问题。但在项目中的幽灵依赖很可能导致一些不符合预期的错误:

同样的,由于npm node_modules扁平化的设计,在不同的软件包依赖同一个依赖包的不同版本时,npm只能将其中一个版本提到根node_modules,其他版本的依赖还是会保留在原先的树结构,这种被保留的npm包就称为npm分身。

npm分身在小项目中很少遇到,但在大型monorepo项目中常见,这会导致以下问题:

由于pnpm的node_modules结构基于软/硬连接而非扁平化算法,所以pnpm的node_modules不会导致幽灵依赖和npm包分身的问题。

pnpm workspace

pnpm workspace 是项目 monorepo 的一种解决方案。采用 pnpm-workspace.yaml 文件来管理多个npm包。

# pnpm-workspace.yaml
packages:
  - apps/*
  - packages/*

_点滴器各部分名称_点滴器原理

在pnpm workspace等monorepo仓库中,其实也存在幽灵依赖的问题。这是monorepo根工程和子仓库的node_modules形成的嵌套结构所造成的,但好在一般monorepo的根工程中不会引入大多业务相关的依赖,所以这个问题并不明显。

workspace工作空间协议

pnpm支持工作空间协议workspace:, 可以将本地的基础包直接link到应用工程中进行调试验证。

当选项被设置为false时,这个协议将特别有用。 在这种情况下,仅当使用workspace:协议声明依赖,pnpm 才会从此 workspace 链接所需的包。

当使用pnpm publish发布包时,pnpm 会动态替换这些workspace:依赖

{
    "dependencies": {
        "foo": "workspace:*",
        "bar": "workspace:~",
        "qar": "workspace:^",
        "zoo": "workspace:^1.5.0"
    }
}

依赖提升设置

部分npm包会刻意依赖幽灵依赖的特性,比如:eslint/prettier,如果你知道只有某些有缺陷的包具有幻影依赖,你可以使用此选项专门提升幻影依赖(推荐做法)

当前版本pnpm 9+中会默认将 eslint/prettier 内置到 .npmrc public-hoist-pattern 配置中

...
'public-hoist-pattern': [
    '*eslint*',
    '*prettier*', 
],

所以eslint/prettier以及其相关的依赖默认都会进行提升:

image.png

pnpm计划在下一个版本(10.x)移除这个默认配置,详情可见


上一条查看详情 +探索CSS响应式设计:从原理到实战
下一条 查看详情 +没有了