pnpm 是一个高效、快速且节省磁盘空间的 JavaScript 包管理器,旨在解决传统包管理器(如 npm 和 Yarn Classic)在性能、磁盘空间使用和依赖管理结构上的不足。它通过创新的内容可寻址存储和非扁平化的 node_modules 结构,提供更高效的依赖安装和管理方式。pnpm 不仅适用于前端和后端项目,还在 Node.js 生态系统中表现出色,广泛用于现代 JavaScript 项目。

以下是对 pnpm 的详细介绍,涵盖其初衷、核心功能、安装方式、与其他包管理器的比较以及使用场景。


一、pnpm 的项目初衷

pnpm 的设计目标是解决传统包管理器(如 npm 和 Yarn Classic)在磁盘空间、安装速度和依赖管理结构上的问题。其核心优势体现在以下三个方面:

1. 节省磁盘空间

pnpm 使用**内容可寻址存储(Content-Addressable Storage, CAS)**来管理依赖项,与 npm 和 Yarn Classic 相比显著减少磁盘空间占用。其工作原理如下:

  • 单一存储位置:所有依赖文件存储在硬盘上的统一位置(通常是 ~/.pnpm-store),不同项目共享同一版本的依赖。
  • 硬链接机制:在项目中安装依赖时,pnpm 使用硬链接将存储中的文件链接到项目的 node_modules 目录,而不复制文件,从而避免占用额外磁盘空间。
  • 增量更新:当依赖更新时,仅存储发生变化的文件。例如,一个包有 100 个文件,新版本只修改了 1 个文件,pnpm 只会添加这 1 个文件,而不是重新存储整个包。

这使得 pnpm 在多项目开发中能够显著节省磁盘空间,尤其是在依赖数量多或项目规模大的场景下。

2. 提高安装速度

pnpm 的安装过程分为三个阶段,与传统包管理器的流程相比更高效:

  1. 依赖解析:识别项目所需的依赖,检查内容可寻址存储中是否已存在,若不存在则下载。
  2. 目录结构计算:根据依赖关系计算出 node_modules 目录的结构。
  3. 链接依赖:从存储区中获取已有的依赖文件,通过硬链接将其加入 node_modules 目录。

相比 npm 和 Yarn Classic 的传统三阶段流程(解析、下载、写入),pnpm 的方法避免了重复下载和写入,从而显著提高了安装速度。

3. 创建非扁平的 node_modules 结构

传统包管理器(如 npm 和 Yarn Classic)会将所有依赖提升到 node_modules 的根目录,导致“扁平化”结构。这种结构可能引发以下问题:

  • 项目可以直接访问非声明的依赖(依赖的依赖),可能导致意外的依赖冲突或安全问题。
  • 依赖结构复杂,难以理解和管理。

pnpm 默认使用**符号链接(symlinks)**创建一个非扁平的 node_modules 结构:

  • 项目的直接依赖通过符号链接放置在 node_modules 根目录。
  • 依赖的依赖(子依赖)存储在 node_modules/.pnpm 目录中,并通过符号链接与直接依赖关联。
  • 这种结构确保只有声明的依赖可以被项目直接访问,增强了隔离性和安全性,同时与 Node.js 的模块解析机制完全兼容。

如果工具不支持符号链接,pnpm 提供 nodeLinker=hoisted 配置,生成类似 npm 的扁平化结构以确保兼容性。


二、pnpm 的核心功能

pnpm 提供了丰富的功能,使其在现代 JavaScript 开发中具有竞争优势。以下是其主要功能的概述,基于功能比较文档:

1. 功能概览

功能 pnpm Yarn npm 备注
Workspace 支持 支持 monorepo 工作流。
隔离的 node_modules pnpm 默认使用隔离结构,增强依赖安全性。
Hoisted node_modules npm 默认使用扁平化结构,pnpm 可通过配置支持。
Plug’n’Play Yarn 默认使用,pnpm 可选支持。
自动安装 peer 依赖 pnpm 和 npm 支持自动安装 peerDependencies。
零安装 Yarn 支持零安装(Zero-Install),pnpm 暂不支持。
修补依赖 pnpm 和 Yarn 支持修改依赖代码。
管理 Node.js 版本 pnpm 通过 pnpm env 命令支持 Node.js 版本管理。
管理自身版本 pnpm 和 Yarn 支持版本自管理。
锁文件 pnpm 使用 pnpm-lock.yaml
内容可寻址存储 Yarn 在特定配置下支持,pnpm 默认使用。
动态包执行 pnpm 使用 pnpm dlx,类似 npxyarn dlx
副作用缓存 pnpm 支持缓存构建副作用,提升重复构建性能。
目录支持(Catalogs) pnpm 独有功能,简化 monorepo 依赖管理。
配置依赖 pnpm 支持配置化依赖管理。
JSR 注册表支持 pnpm 和 Yarn 支持 JSR 注册表。
脚本运行前自动安装 pnpm 在运行脚本前自动安装依赖,Yarn 的 PnP 可确保依赖最新。
钩子(Hooks) pnpm 和 Yarn 支持自定义钩子。
列出许可证 pnpm 通过 pnpm licenses list 支持,Yarn 需要插件。

2. 独特功能解析

  • 内容可寻址存储:pnpm 的核心优势,通过单一存储和硬链接机制减少磁盘占用。
  • 非扁平 node_modules:通过符号链接实现依赖隔离,防止意外访问子依赖。
  • 副作用缓存:缓存构建过程中的副作用(如编译结果),提升重复构建效率。
  • 目录(Catalogs):在 monorepo 项目中简化依赖版本管理,适合大型项目。
  • Node.js 版本管理:通过 pnpm env 命令管理 Node.js 版本,方便开发和 CI 环境配置。
  • 自动安装 peer 依赖:简化依赖管理,减少手动配置的工作量。

三、pnpm 的安装方式

pnpm 提供了多种安装方式,适用于不同操作系统和使用场景。以下是详细的安装方法:

1. 前提条件

  • 如果不使用独立脚本或 @pnpm/exe,需要安装 Node.js v18.12 或更高版本
  • 某些安装方式(如 Corepack)需要额外配置。

2. 使用独立脚本安装

无需预装 Node.js,可通过以下脚本安装 pnpm:

  • Windows(PowerShell)
    Invoke-WebRequest https://get.pnpm.io/install.ps1 -UseBasicParsing | Invoke-Expression
    
    注意:Windows Defender 可能阻止可执行文件,建议使用 npm 或 Corepack。
  • POSIX 系统(Linux/macOS)
    curl -fsSL https://get.pnpm.io/install.sh | sh -
    
    或使用 wget:
    wget -qO- https://get.pnpm.io/install.sh | sh -
    
  • Docker 容器
    wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.bashrc" SHELL="$(which bash)" bash -
    
  • 安装特定版本
    curl -fsSL https://get.pnpm.io/install.sh | env PNPM_VERSION=<version> sh -
    

3. 使用 Corepack 安装

Node.js v16.13+ 自带 Corepack,需先更新 Corepack:

npm install --global corepack@latest

启用 pnpm:

corepack enable pnpm

固定项目版本:

corepack use pnpm@latest-10

4. 使用其他包管理器

  • npm
    npm install -g pnpm@latest-10
    
    或安装无需 Node.js 的版本:
    npm install -g @pnpm/exe@latest-10
    
  • Homebrew(macOS/Linux):
    brew install pnpm
    
  • winget(Windows):
    winget install -e --id pnpm.pnpm
    
  • Scoop(Windows):
    scoop install nodejs-lts pnpm
    
  • Chocolatey(Windows):
    choco install pnpm
    
  • Volta
    volta install pnpm
    

5. 兼容性

pnpm 的版本与 Node.js 的兼容性如下:

Node.js 版本 pnpm 8 pnpm 9 pnpm 10
Node.js 14
Node.js 16 ✔️
Node.js 18 ✔️ ✔️ ✔️
Node.js 20 ✔️ ✔️ ✔️
Node.js 22 ✔️ ✔️ ✔️

6. 设置别名

为方便使用,可设置 pn 作为 pnpm 的别名:

  • POSIX 系统
    .bashrc.zshrcconfig.fish 中添加:
    alias pn=pnpm
    
  • Windows(PowerShell)
    notepad $profile.AllUsersAllHosts
    
    添加:
    set-alias -name pn -value pnpm
    

7. 更新与卸载

  • 更新 pnpm
    pnpm self-update
    
  • 卸载 pnpm
    找到 pnpm 安装路径(使用 which pnpmwhere.exe pnpm.*),删除相关文件(如 pnpm.cmdpnpx.cmd),然后重新安装或完全移除。

四、pnpm 的优势与使用场景

1. 优势总结

  • 节省磁盘空间:通过内容可寻址存储和硬链接机制,减少重复存储。
  • 快速安装:分阶段安装流程,效率高于 npm 和 Yarn Classic。
  • 依赖隔离:非扁平的 node_modules 结构,防止意外访问子依赖。
  • 功能丰富:支持 monorepo、Node.js 版本管理、副作用缓存等现代开发需求。
  • 兼容性强:与 Node.js 生态系统无缝集成,支持多种安装方式和配置。

2. 使用场景

  • 大型项目和 monorepo:pnpm 的目录(Catalogs)和 Workspace 支持使其非常适合管理复杂的大型项目。
  • 磁盘空间受限环境:如 CI/CD 服务器或开发者的本地机器,pnpm 能显著减少存储需求。
  • 高性能需求:快速安装和副作用缓存适合频繁构建的场景。
  • 需要严格依赖隔离的项目:非扁平的 node_modules 结构确保依赖安全性和可预测性。

五、与其他包管理器的比较

pnpm 在功能和性能上与 npm 和 Yarn Classic 相比有明显优势:

  • npm:传统包管理器,扁平化 node_modules 结构,磁盘占用高,安装速度较慢,缺少高级功能(如副作用缓存、Node.js 版本管理)。
  • Yarn Classic:支持 Workspace 和 Plug’n’Play,但磁盘空间优化不如 pnpm,部分功能需要插件支持。
  • pnpm:结合了 npm 的稳定性和 Yarn 的现代化功能,增加了内容可寻址存储、非扁平化结构等独特特性。

六、总结

pnpm 是一个现代化、高效的 JavaScript 包管理器,通过内容可寻址存储、非扁平的 node_modules 结构和快速安装流程,解决了传统包管理器在磁盘空间、安装速度和依赖管理上的痛点。其丰富的功能(如副作用缓存、Node.js 版本管理、Catalogs)使其成为 monorepo、大型项目和性能敏感场景的理想选择。无论是在本地开发还是 CI/CD 环境中,pnpm 都能提供卓越的性能和灵活性,是现代 JavaScript 开发者的优选工具。

七、参考资料

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐