pnpm 命令详解:安装、依赖管理、工作区与从 npm 迁移(Linux/macOS/Windows)

大型 Node.js 项目在每次都保留相同包的各自副本时,会消耗惊人的磁盘空间。pnpm 通过将包文件存储在共享的内容寻址存储中,并将它们链接到每个项目来解决这一问题。它使用 npm registry,并与相同的 package.json 文件协同工作,同时提供更严格的依赖处理和内置的工作区支持。

本指南讲解如何在 Linux、macOS 和 Windows 上安装 pnpm,然后用它创建项目、安装 Node.js 运行时、添加和更新包、运行脚本、从 npm 迁移,以及在 monorepo 中协作。

语法

该命令的一般形式如下:

pnpm  [options]

大多数 pnpm 命令都有直接的 npm 等价命令。例如,pnpm install 安装项目依赖,pnpm add 记录一个新依赖,pnpm run 执行 package.json 中的脚本。

pnpm 如何存储包

pnpm 将包文件保存在你机器上的共享存储中。项目链接到这些文件,而不是把相同的包内容复制到每个 node_modules 目录。如果十个项目使用同一包版本,pnpm 只存储一次它的文件。

生成的 node_modules 布局还能防止包导入它们未声明的依赖。这种更严格的行为有助于暴露缺失的依赖声明,而 npm 扁平的依赖布局有时会隐藏这些。

你仍然要把 package.json 和 pnpm-lock.yaml 锁文件提交到版本控制。共享存储和生成的 node_modules 目录不应提交。

安装 pnpm

pnpm 12 是一个原生可执行文件。安装后它在没有 Node.js 的情况下也能运行,因此你可以在还没有 Node.js 的机器上设置 pnpm,之后再添加运行时。npm 方式是个例外,它需要 Node.js 22.13 或更新版本来运行安装程序。

安装独立版本

独立安装程序会下载适用于你平台的 pnpm 二进制文件,不需要 Node.js 存在。在 Linux、macOS 和其他 POSIX 系统上,运行:

curl -fsSL https://get.pnpm.io/install.sh 
|
 sh -

如果未安装 curl,wget 以相同方式工作:

wget -qO- https://get.pnpm.io/install.sh 
|
 sh -

要安装特定发布版本,为脚本设置 PNPM_VERSION 变量:

curl -fsSL https://get.pnpm.io/install.sh 
|
 env 
PNPM_VERSION
=
12.3.4 sh -

在 Windows PowerShell 上,使用:

Invoke-WebRequest
 
https
:
//
get
.
pnpm
.
io
/
install
.
ps1
 
-UseBasicParsing
 
|
 
Invoke-Expression

Microsoft Defender 有时会阻止以这种方式安装的 pnpm 可执行文件。在 Windows 上,下面用 npm 的方式是更安全的选择。

用 npm 安装

如果你已有 Node.js 22.13 或更新版本,运行 npm 安装程序:

npx get-pnpm

运行安装程序需要 Node.js,但之后运行 pnpm 不需要。如果你的 Node.js 版本较旧,请参阅我们在 Ubuntu 26.04 上安装 Node.js 和 npm 的指南,或者让 pnpm 按下一节所述为你安装运行时。

更新已有安装

从 pnpm v11.10.0 起,你可以就地迁移到当前发布版本:

pnpm self-update

安装程序完成后打开一个新终端,然后验证命令:

pnpm --version
12.3.4

为项目固定 pnpm 版本

在 package.json 中记录 pnpm 版本,可以让贡献者和 CI 任务保持在同一个发布版本上。创建项目时添加该字段:

pnpm init --init-package-manager

这会写入一个带有确切 pnpm 版本的 packageManager 条目。在一个已有该条目的项目中,pnpm self-update 会更新该条目而不动你的全局安装,并且 pnpm 在下次命令时切换到记录的发布版本。

Corepack 也可以固定包管理器版本,并且它仍然有效。它在 pnpm 的位置安装一个 JavaScript 填充(shim),因此每次 pnpm 调用都会先启动 Node.js 再运行 pnpm 本身。pnpm 文档现在建议直接安装 pnpm。

用 pnpm 安装 Node.js

pnpm 可以自己安装并管理 Node.js 运行时。这在通过独立脚本获得 pnpm 且完全没有 Node.js 的机器上很方便。

安装当前 LTS 发布版本并用 -g 把它放到你的 PATH 上:

pnpm runtime 
set
 node lts -g

你也可以指定一个主版本或要求最新发布版本:

pnpm runtime 
set
 node 
22
 -g

pnpm runtime 
set
 node latest -g

用通常的方式检查结果:

node --version
v24.21.0

同一命令也能安装其他运行时,例如 pnpm runtime set deno 2 -g 或 pnpm runtime set bun latest -g。

初始化项目

创建一个目录并初始化 package.json 文件:

mkdir example-app

cd
 example-app

pnpm init

pnpm 写入一个带默认字段的 package.json 且不询问任何问题。当你也希望项目被固定到你正在运行的 pnpm 版本时,添加 --init-package-manager。如果你已有一个用 npm 创建的项目,pnpm 可以不经任何转换就使用其现有的 package.json。

安装包

用 pnpm add 添加运行时依赖:

pnpm add express

pnpm 安装该包,把它记录到 package.json 的 dependencies 下,并创建或更新 pnpm-lock.yaml。

用 -D(--save-dev 的简写)添加开发工具:

pnpm add -D eslint

要安装特定包版本,在 @ 后追加它:

pnpm add express@5.1.0

当你克隆一个已有的 pnpm 项目时,用以下命令安装其包文件中声明的所有内容:

pnpm install

对于 CI 或其他可复现环境,防止锁文件被更改:

pnpm install --frozen-lockfile

当 package.json 和 pnpm-lock.yaml 不一致时,该命令会失败,而不是解析出新的依赖树。一旦 pnpm 检测到 CI 环境,它会自动开启此模式,因此该标志主要在你希望本地也有相同行为时才有意义。

安装全局包

用 -g 全局安装一个命令行工具:

pnpm add -g http-server

如果 pnpm 报告找不到全局二进制目录,运行:

pnpm setup

打开一个新终端让更新后的 PATH 生效,然后重复全局安装。把应用依赖保持在每个项目的本地,全局安装只保留跨项目需要的命令。

更新与检查包

检查哪些依赖有可用更新:

pnpm outdated

在 package.json 的版本范围内更新依赖:

pnpm update

要将一个包更新到其最新发布的版本并更改其声明的范围,运行:

pnpm update express --latest

用以下命令列出当前项目的直接依赖:

pnpm list --depth 
0

当你需要理解某个包为何存在时,使用:

pnpm why express

移除包

用以下命令把依赖从 node_modules、package.json 和锁文件中移除:

pnpm remove express

对于全局安装的包,添加 -g:

pnpm remove -g http-server

运行脚本与包二进制

pnpm 像 npm 一样读取 package.json 的 scripts 段。给定这个文件:

{


  
"scripts"
:
 
{


    
"start"
:
 
"node index.js"
,


    
"lint"
:
 
"eslint ."


  
}


}

用以下命令运行具名脚本:

pnpm run lint

当脚本名与 pnpm 命令不冲突时,可以省略 run:

pnpm start

用 pnpm exec 运行当前项目中安装的二进制:

pnpm 
exec
 eslint .

对于你不想作为依赖添加的临时包,使用 pnpm dlx:

pnpm dlx create-vite@latest my-app

pnpm exec 运行已有的本地依赖,而 pnpm dlx 临时获取一个包并运行其命令。

从 npm 迁移项目

从包含 package-lock.json 的项目生成 pnpm 锁文件:

pnpm import

该命令读取 npm 锁文件并创建 pnpm-lock.yaml。接下来安装导入的依赖树:

pnpm install

在移除 package-lock.json 之前运行项目的测试和构建。提交 pnpm-lock.yaml,并将 CI 命令从 npm ci 更新为 pnpm install --frozen-lockfile。

如果仓库在其 packageManager 字段中记录了包管理器,用以下命令更新该固定:

pnpm self-update

使用 pnpm 工作区

pnpm 工作区把一个仓库中的多个包组合起来。在仓库根目录的 pnpm-workspace.yaml 中定义其包位置:

packages
:


  
- 
"apps/*"


  
- 
"packages/*"

从根目录为每个工作区包安装依赖:

pnpm install

递归地对定义了该脚本的所有包运行脚本:

pnpm --recursive run build

当命令应针对单个包时,使用 --filter:

pnpm --filter web-app add zod

pnpm --filter web-app run test

过滤器可以按名称、目录、依赖关系或更改的文件选择包,这使它们在较大的 monorepo 和 CI 任务中很有用。

快速参考

如需可打印的快速参考,请参阅 pnpm 速查表。

TaskCommand
检查 pnpm 版本pnpm --version
更新 pnpm 自身pnpm self-update
安装 Node.js 运行时pnpm runtime set node lts -g
初始化项目pnpm init
初始化并固定 pnpm 版本pnpm init --init-package-manager
安装项目依赖pnpm install
用未更改的锁文件安装pnpm install --frozen-lockfile
添加依赖pnpm add
添加开发依赖pnpm add -D
添加全局包pnpm add -g
显示过时包pnpm outdated
更新包pnpm update
将一个包更新到最新版pnpm update --latest
移除包pnpm remove
运行脚本pnpm run