pnpm-hoisting


前言

pnpm 的依赖管理方式与传统的 npmyarn 有显著的不同,它采用了一种叫做 hoisting(提升)和 content-addressable storage(内容可寻址存储)的策略来管理依赖。以下是对 pnpm 的 hoisting 依赖管理方式的详细解析。

1. 传统的依赖管理方式(npm/yarn)

npmyarn 中,依赖管理通常是 扁平化 的。也就是说,所有的依赖项都被安装在项目的 node_modules 根目录中,这样即使某个依赖在不同的子依赖中出现多次,也只会在根目录下安装一次。这种方式虽然减少了依赖项的冗余,但是可能会导致一些依赖冲突,尤其是当不同依赖版本之间存在不兼容问题时。

2. pnpm 的独特之处:内容可寻址存储

pnpm 使用了一种基于内容可寻址存储的依赖管理方式。具体来说,pnpm 会将所有的依赖项存储在一个全局的 store 中,store 的位置通常位于 ~/.pnpm-store 或者自定义的路径下。

每个依赖包在 store 中只存储一次,即使在多个项目中使用了相同版本的依赖,也只会存储一份。这种方式不仅节省了磁盘空间,还加快了安装速度,因为依赖项可以被多个项目共享。

3. pnpm 的 hoisting 策略

虽然 pnpm 在存储上是全局共享的,但它在项目中的依赖管理上使用了一种类似 hoisting 的方式:

  • 项目级别的 node_modules 目录

    • 在项目的 node_modules 目录下,pnpm 不会像 npmyarn 那样将所有依赖扁平化安装到根目录中。相反,它会为每个依赖创建一个隔离的符号链接。
    • 例如,假设你有一个依赖树 A -> B -> C,其中 A 依赖于 B,而 B 依赖于 C。在 pnpm 中,项目的 node_modules 目录可能会看起来像这样:
      node_modules/
        .pnpm/
          A@1.0.0/
            node_modules/
              B/
          B@1.0.0/
            node_modules/
              C/
          C@1.0.0/
      
    • 这意味着 BC 不会被直接安装在项目的 node_modules 根目录中,而是被隔离在特定的路径中。
  • 符号链接(Symlinks)

    • pnpm 通过符号链接将依赖项链接到项目的 node_modules 目录中。这意味着,在项目的 node_modules 中,BC 都是指向 store 中实际位置的符号链接。
    • 例如,node_modules/B 实际上是一个符号链接,指向 node_modules/.pnpm/B@1.0.0/node_modules/B
  • 父级和兄弟依赖的访问

    • 由于依赖项被隔离并通过符号链接进行管理,某些情况下,父级或兄弟依赖可能无法直接访问。pnpm 的这种依赖隔离模型使得包只能访问自己声明的依赖,而不是依赖于其他包可能会引入的某些包版本。这种严格的模块隔离可以帮助防止“隐式依赖”问题,但可能会导致一些包在无法找到其依赖时出现问题。

4. Hoisting 的优化

pnpm 还提供了 hoisting 配置选项,允许开发者控制 hoisting 的行为:

  • shamefully-hoist

    • pnpm 的默认行为是保持依赖项的隔离,这样可以防止“隐式依赖”问题。然而,有些项目(特别是遗留项目)可能依赖于 npm 的扁平化依赖结构。在这些情况下,你可以启用 shamefully-hoist 选项,让 pnpm 模拟传统的 npmyarn 的行为,将所有依赖提升到根目录下,以兼容这些项目。

    启用 shamefully-hoist

    pnpm install --shamefully-hoist
    
  • public-hoist-pattern

    • 你可以通过 public-hoist-pattern 指定哪些依赖项应该被提升到项目根目录下。这允许你对 hoisting 进行更精细的控制,以满足项目的特定需求。

    配置 public-hoist-pattern

    hoist-pattern:
      - '@babel/*'
      - 'eslint'
    

5. 优势与挑战

优势

  • 磁盘节省:由于所有项目共享同一个 storepnpm 显著减少了磁盘使用。
  • 安装速度快:因为依赖项是共享的,安装已经存在的依赖项几乎是即时完成的。
  • 模块隔离:防止了“隐式依赖”,强制每个包正确声明自己的依赖。

挑战

  • 兼容性问题:由于 pnpm 的依赖管理更加严格,一些依赖于 npm 扁平化结构的包可能会在 pnpm 中出现问题。
  • 符号链接:在某些开发环境中,符号链接可能会引发一些问题,尤其是在不完全支持符号链接的文件系统上。

总结

pnpm 的 hoisting 依赖管理方式通过内容可寻址存储和符号链接大大提高了依赖管理的效率和可靠性。虽然这种方式在某些情况下可能带来一些兼容性挑战,但它也通过模块隔离和资源共享带来了巨大的优势。如果你正在处理复杂的依赖树或希望优化项目的依赖管理,pnpm 是一个值得考虑的工具。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pnpm-workspace 是 pnpm 包管理器的一个功能,它允许你在一个项目中管理多个子包。了解了这一点后,我们可以根据引用内容来回答你的问题。 要启用 pnpm 的 workspace 功能,需要在工程根目录下创建一个名为 pnpm-workspace.yaml 的配置文件,并且在其中指定工作空间的目录。在你提供的示例中,这个文件内容如下:packages: - 'packages/*'。这表示你的项目的子包都放在名为 packages 的目录下。 在使用 pnpm 的 workspace 功能时,当你运行 pnpm publish 命令时,会自动将 package.json 中的 workspace 字段修正为对应的版本号。这样可以确保子包之间的依赖关系正确。 为了说明这一过程,让我们来看一下你提供的项目结构示例: /pnpm_workspace ├── package.json ├── packages │ ├── add-one │ │ ├── index.js │ │ ├── package.json │ │ └── test.test.js │ ├── add-two │ │ ├── index.js │ │ ├── package.json │ │ └── test.test.js │ └── adder │ ├── index.js │ └── package.json ├── pnpm-lock.yaml └── pnpm-workspace.yaml 现在,让我们通过一系列步骤来构建这样一个项目: 1. 首先,创建一个名为 pnpm_workspace 的文件夹,并进入该文件夹。 2. 在该文件夹中运行 pnpm init -y 命令,以初始化根目录的项目。 3. 创建一个名为 pnpm-workspace.yaml 的文件,并在其中添加 packages: - 'packages/*' 的内容,以指定工作空间的目录。 4. 在 packages 目录下创建三个子包(add-one、add-two 和 adder),并分别初始化它们的 package.json 文件。 5. 在 adder 子包中,通过运行 pnpm add add-one --workspace 和 pnpm add add-two --workspace 命令,将 add-one 和 add-two 子包添加为 adder 的依赖。 通过以上步骤,你已经成功构建了一个具有 pnpm 的 workspace 功能的项目。 请注意,以上过程只是一个示例,你可以根据实际需要进行相应的调整和修改。希望这个回答对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值