一文了解 pnpm,并快速上手操作!

0 阅读6分钟

PNPM 简介

npm(Node Package Manager)中文名为 Node 包管理器,是 Node.js 官方自带、全球最大的 JavaScript 软件包管理工具,也是前端 / Node.js 开发最基础、最常用的工具之一。

pnpm(Performant NPM),翻译过来就是高性能的 npm,旨在解决传统包管理器(如 npm 和 Yarn Classic)在性能、磁盘空间使用和依赖管理结构上的不足。

目前,pnpm 已成为前端生态最受欢迎的包管理器之一,被 Vue、Vite、Nuxt、Next.js、Svelte 等顶级开源项目,以及字节、阿里、腾讯等大厂的前端团队广泛采用,是公认的「当前最先进的包管理工具」。

pnpm 比传统方案安装包的速度快了两倍,以下是官方给出的benchmarks(对比了npm, pnpm, Yarn Classic, and Yarn PnP),在多种常见情况下,执行install的速度比较

Graph of the alotta-files results

pnpm 的核心优势,源于它从底层架构上彻底重构了依赖管理方式,精准解决了传统工具的多个顽疾:

1. 解决“磁盘空间浪费”与“安装速度慢”

使用 npm 时,若你有 100 个项目都依赖同一个包,硬盘中就会重复存储 100 份该依赖包的完整副本,造成大量磁盘空间浪费。

pnpm 则通过硬链接搭配符号链接(软链接) 的机制,从根源上避免依赖重复拷贝,同时大幅提升安装效率:

  1. 增量存储:针对同一依赖包的不同版本,pnpm 仅会存储版本间存在差异的文件。例如新版本仅修改了单个文件,pnpm update 只需新增这一个文件至存储仓库,无需完整保存整个依赖包。
  2. 全局共享:pnpm 会在本地维护一个全局内容寻址存储库(默认路径为 ~/.pnpm-store),所有项目用到的依赖包,在全局仓库仅留存一份真实物理文件。
  3. 零拷贝:项目安装依赖时,pnpm 不会复制文件,而是通过硬链接(项目里 /node_modules/.pnpm/)指向全局仓库中的源文件;
  4. 兼容结构:最后通过软链接搭建符合 Node.js 规范的 node_modules 根目录结构,让项目和构建工具能正常识别依赖,同时为依赖隔离打下基础。

2. 解决“幽灵依赖” (Phantom Dependencies)

幽灵依赖是 npm 体系中一个极具隐患的问题。npm 会通过依赖扁平化(Dependency Hoisting) 机制,将间接依赖提升至 node_modules 根目录,这就导致项目可以直接引入并使用那些并未在自身 package.json 中声明的依赖包。这类依赖之所以能被访问,只是因为它们是其他直接依赖的子依赖,并在依赖提升后暴露在了根目录下。

幽灵依赖看似能简化开发,实则暗藏风险:

  1. 依赖版本不可控,易造成构建结果不稳定,排查问题时需要逐层追溯依赖树,调试与维护成本极高;
  2. 不同环境下依赖结构可能存在差异,极易出现本地正常、线上构建失败的情况;
  3. 间接依赖若存在安全漏洞,开发者往往难以感知,会带来潜在的安全风险。

pnpm 则通过虚拟存储(Virtual Store) 机制,从底层解决这一痛点:

它在项目内模拟传统 node_modules 的嵌套结构,同时在 .pnpm 目录下以包名 + 版本号的形式为每个版本创建独立文件夹,通过硬链接从全局仓库精准关联对应版本的真实文件;

再通过根目录node_modules 的符号链接,构建出严格的依赖隔离层级。

最终实现不同版本的依赖独立存放、精准引用、互不干扰,从根源上杜绝版本冲突,完美支持 Monorepo 场景下多子包的不同版本依赖,保障复杂依赖关系下项目的稳定运行。

3. 解决“多版本依赖冲突”

在实际项目中,常会出现不同第三方库依赖同一依赖包不同版本的情况(如部分组件库依赖 React 17,另一部分依赖 React 18)。

npm 针对该问题采用嵌套 node_modules 的基础隔离方案,将不兼容的版本安装在对应第三方包的内部目录中。但此方案存在明显缺陷:依赖目录结构变得杂乱无章,Windows 系统下易因路径过长导致报错,大量重复嵌套的依赖会造成磁盘空间浪费;加之 npm 保留的扁平化逻辑无法实现严格隔离,版本冲突与项目运行异常的风险始终存在。

pnpm 使用了一种叫做虚拟存储(Virtual Store) 机制。它在项目内模拟传统 node_modules 的嵌套结构,同时在 .pnpm 目录下以包名 + 版本号的形式为每个版本创建独立文件夹,通过硬链接从全局仓库精准关联对应版本的真实文件;再通过根目录 node_modules 的符号链接,构建出严格的依赖隔离层级。最终实现不同版本的依赖独立存放、精准引用、互不干扰,从根源上杜绝版本冲突,保障复杂依赖关系下项目的稳定运行。

总结:三层架构

层级位置内容性质作用
L1: 全局仓库~/.pnpm-store真实文件节省磁盘空间,所有项目共享。
L2: 项目仓库node_modules/.pnpm硬链接管理项目内复杂的依赖版本和嵌套关系。
L3: 暴露接口node_modules/符号链接方便构建工具寻找依赖。

pnpm.png

PNPM 安装

必须先安装 Node.js(npm 自带),先去官网装:nodejs.org/(选 LTS 版本)

# 安装 pnpm
npm install -g pnpm
​
# 检查是否安装成功
pnpm -v
​
# 初始化 pnpm
pnpm setup
​
# 尝试升级
pnpm self-update

# 国内换源
pnpm config set registry https://registry.npmmirror.com/

PNPM 快速上手

以下是列出常用的命令,具体可以参考官网管理依赖

安装依赖包

pnpm add <pkg>
命令说明
pnpm add sax安装并保存到 dependencies(生产依赖)
pnpm add -D sax安装并保存到 devDependencies(开发依赖)
pnpm add -O sax安装并保存到 optionalDependencies(可选依赖)
pnpm add -g sax全局安装
pnpm add sax@next安装 next 标签对应的版本
pnpm add sax@3.0.0安装指定版本 3.0.0

安装项目全部依赖

pnpm install
# 或简写 pnpm i

更新依赖

pnpm update
# 或简写 pnpm up
命令说明
pnpm uppackage.json 约定的版本范围内,更新所有依赖
pnpm up --latest忽略版本范围约束,更新所有依赖到最新版
pnpm up foo@2foo 更新到 v2 系列的最新版本
pnpm up "@babel/*"更新 @babel scope 下的所有依赖

删除依赖

pnpm remove # 或简写 pnpm rm
pnpm uninstall # 或简写 pnpm un
# remove 和 uninstall 作用上完全等价

运行脚本

  • 执行 package.json 中定义的脚本:

    pnpm run <script>
    
  • 运行测试脚本:

    pnpm test
    
  • 运行启动脚本:

    pnpm start
    # 或
    pnpm run start
    

初始化 / 创建项目

create-*@foo/create-* 模板快速创建项目:

pnpm create <starter> [项目名]

示例:

pnpm create react-app my-app

常用进阶小技巧

  1. 清理无用依赖

    pnpm prune
    
  2. 查看依赖来源(排查冲突用)

    pnpm why react
    
  3. 删除 node_modules(比手动删更快)

    pnpm store prune