博客
关于我
pnpm的设计与npm的对比
阅读量:797 次
发布时间:2023-03-03

本文共 3291 字,大约阅读时间需要 10 分钟。

pnpm的设计与npm的对比

pnpm与npm对比科普(macOS系统下)

本文将从全局包管理、配置方式、依赖安装机制等方面对比pnpm与npm在macOS系统下的工作原理与使用差异,帮助开发者更好地理解两者各自的优缺点。


1. 全局包管理与安装路径

npm的全局包管理
  • 默认安装位置: npm在macOS上默认将全局包安装到/usr/local/lib/node_modules。用户可以通过命令npm config get prefix确认npm的安装路径。
  • 命令注册: 安装全局包后,npm会自动将包内的可执行文件链接到/usr/local/bin,从而可以在终端直接调用相应命令。
pnpm的全局包管理
  • 配置获取: pnpm的配置来源更加多元,可以从命令行参数、环境变量以及.npmrc文件中读取配置。
  • 初始化与配置写入: 在执行pnpm setup后,pnpm会将相关配置写入用户的shell配置文件(如~/.zshrc),配置示例如下: # pnpmexport PNPM_HOME="/Users/xxx/Library/pnpm"export PATH="$PNPM_HOME:$PATH"# pnpm end

    这样,pnpm管理的包的可执行文件所在目录会被加入到环境变量PATH中,其优先级通常高于npm的全局bin目录。


2. 依赖安装与模块解析差异

npm与yarn的“幽灵依赖”
  • 依赖提升机制: 在使用npm或yarn安装依赖时,某些依赖包会被“提升”到项目根目录下的node_modules中。这种现象有时会让开发者感觉有“幽灵依赖”,即某些包没有在package.json中明确声明,却可以直接引用(通常是由于扁平化依赖树的原因)。
  • 实际效果: 例如,当使用yarn安装时,@babel/runtime等包可能会被提升,使得在项目其他地方直接引用变得可能。
pnpm的严格依赖解析
  • 无依赖提升(No Hoisting): 与npm和yarn不同,pnpm不会自动将依赖包提升到项目根目录的node_modules中。pnpm使用符号链接(symlink)的方式,将包安装在一个全局的内容寻址存储区中,然后在项目中建立指向这些存储区的链接。
  • 优势:
    • 一致性: 这种安装方式使得每个包都在自己的独立目录中,从而避免了因依赖扁平化带来的版本冲突或隐式依赖问题。
    • 节省磁盘空间: 多个项目共享同一份存储,减少重复安装同一依赖的情况。
  • 可能的问题:
    • 某些工具或脚本可能默认依赖于npm或yarn的依赖提升机制,导致在pnpm环境下找不到某些预期的“幽灵依赖”。例如,在使用pnpm时发现node_modules下并不存在@babel/runtime包,而使用yarn安装时则可以正常引用。

3. pnpm的详细设计

3.1 pnpm的核心理念
内容寻址存储(Content-Addressable Storage)
  • 概念: pnpm采用内容寻址存储的方式管理所有已下载的依赖包。每个包在存储中都有一个基于其内容生成的唯一标识(哈希值),确保相同内容的包只存储一份。
  • 优势:
    • 节省磁盘空间: 多个项目如果依赖相同版本的包,pnpm只需要存储一份,而不是每个项目各自保存一份副本。
    • 提高安装速度: 当缓存命中时,无需重复下载和解压,能显著加快依赖安装过程。
无依赖提升的Node Modules
  • 严格的依赖树: pnpm不会将依赖提升到项目根目录的node_modules中。每个包都保留在自己的独立目录中,并通过符号链接构建依赖树。
  • 实现方式:
    • 符号链接: pnpm在项目的node_modules目录下为每个依赖创建符号链接,链接指向全局内容寻址存储中的实际文件。
    • 隔离依赖: 这种方式确保了包之间的依赖关系更加明确,防止了隐式依赖问题,使项目的依赖结构更加透明和可控。
3.2 pnpm的架构设计与工作原理
全局缓存与共享存储
  • 全局缓存目录: pnpm会在用户目录下创建一个专用的缓存目录(例如~/.pnpm-store或用户配置的PNPM_HOME),用于存放所有下载的包文件。
  • 共享机制: 不同项目需要相同的依赖时,pnpm直接利用全局缓存中的内容而不是重复下载。即使是离线模式,也可以通过缓存进行安装。
安装过程优化
  • 硬链接与软链接: pnpm倾向于使用硬链接来实现文件共享,这种方式比软链接更高效且可靠。但在某些跨分区或特定文件系统环境下,pnpm会选择使用软链接作为替代。
  • 原子性安装: pnpm在安装过程中采用原子操作,确保在安装或更新依赖时,不会出现中间状态导致项目无法正常运行的情况。
多版本共存
  • 依赖隔离: 由于不进行依赖提升,每个模块可以维护其独立的依赖树。这意味着不同的包可以依赖同一库的不同版本而互不干扰,解决了版本冲突的问题。
  • 平行安装: pnpm充分利用CPU多核的优势,支持并行下载和安装依赖,进一步加快了依赖管理效率。
3.3 pnpm配置与使用技巧
配置文件与环境变量
  • .npmrc支持: pnpm支持读取.npmrc文件中的配置,这使得很多原本在npm中使用的配置项也可以在pnpm中生效。例如,私有仓库地址、认证信息等都可以直接迁移。
  • 环境变量: 用户可以通过设置环境变量(例如PNPM_HOME)来控制pnpm的行为和存储位置,确保与系统其他工具的集成。
CLI命令与常用参数
  • 基本命令:
    • pnpm install:安装项目所有依赖。
    • pnpm add <package>:添加新的依赖到项目,并自动更新package.json
    • pnpm update:更新已安装的依赖到符合版本规则的最新版本。
    • pnpm remove <package>:移除项目中的指定依赖。
  • 高级参数:
    • --frozen-lockfile:确保锁定文件与项目依赖严格一致,防止意外版本更新。
    • --filter:用于多包仓库(monorepo)场景,允许针对特定子项目执行操作。
多包仓库(Monorepo)支持
  • 工作区功能: pnpm原生支持多包仓库,可以在单个仓库中管理多个相互依赖的包。
  • 优势:
    • 一致性:所有包共用同一份依赖版本声明和锁定文件,减少冲突。
    • 高效:跨包引用使用符号链接,无需重复安装。

4. 优势与局限对比

pnpm的优势
  • 性能提升:
    • 利用全局内容寻址存储,安装速度更快,并且磁盘占用更少。
  • 依赖管理严格:
    • 明确的依赖树结构帮助开发者及早发现未声明的依赖,从而提高项目的稳定性和可维护性。
  • 更高的环境一致性:
    • 由于不进行依赖提升,项目在不同机器上的表现更加一致,减少因环境差异引起的问题。
npm的优势
  • 生态成熟:
    • npm是最早的Node.js包管理器,拥有最广泛的社区支持和丰富的文档。
  • 依赖提升的便利性:
    • 对于某些依赖要求较为宽松的项目,依赖提升可以简化包引用,但这同时也可能带来隐患。
  • 兼容性:
    • 许多第三方工具或脚本默认假定依赖被提升,使用npm(或yarn)时可能会更加顺畅。

5. 实践建议

  • 选择合适的工具:
    • 如果你的项目对依赖管理要求严格、需要确保环境的一致性,并且愿意适应较为严格的包解析规则,那么pnpm是一个不错的选择。
    • 对于快速原型开发或已有大量依赖提升习惯的项目,npm或yarn可能会更顺手。
  • 注意配置管理:
    • 无论使用哪种工具,都建议定期检查配置文件(如.npmrc.yarnrc~/.zshrc)中的设置,确保路径和环境变量配置正确,避免工具之间的冲突。
  • 应对“幽灵依赖”问题:
    • 在pnpm中,由于依赖不会被自动提升,如果遇到某些包引用不到的问题,应检查package.json中是否正确声明了所有依赖,必要时可以显式安装缺失的依赖包。

总结

在macOS系统下,npm和pnpm各有优缺点。npm的传统方式使得全局包管理和依赖提升相对直观,但可能隐藏依赖关系带来的风险;而pnpm则通过独特的全局内容寻址存储和无依赖提升机制,实现了更高的效率和一致性,但同时也要求开发者对依赖管理有更明确的认知。

转载地址:http://jzxfk.baihongyu.com/

你可能感兴趣的文章