Chapter 02

安装 pnpm 的 5 种姿势

推荐 Corepack——Node 16.10+ 自带,一行命令锁版本,CI/本地/Docker 都能用同一个 pnpm 版本。

方式一:Corepack(推荐)

corepack enable                   # 一次性启用
corepack prepare pnpm@9.12.0 --activate

pnpm --version                   # 9.12.0

更好的玩法:在项目 package.json 锁定版本,任何成员 cd 进来会自动用对应的 pnpm:

{
  "packageManager": "pnpm@9.12.0"
}
Corepack 为什么
Node 官方项目,把 npm/pnpm/yarn 统一托管,packageManager 字段是它的协议。团队里有人用 pnpm 8 有人用 9 是灾难;锁版本后执行 pnpm install Corepack 会自动下对应版本——不需要每人手工升级。

方式二:独立安装脚本

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

# Windows PowerShell
iwr https://get.pnpm.io/install.ps1 -useb | iex

不走 Node,pnpm 变成全局二进制,启动更快。适合当主力。

方式三:npm 全局

npm install -g pnpm

最直观,但依赖当前 Node 版本——换 Node 版本要重装一次。

方式四:Homebrew / Scoop

# macOS
brew install pnpm

# Windows
scoop install pnpm

方式五:Docker 基础镜像

FROM node:20-alpine
RUN corepack enable
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

pnpm init 创建项目

mkdir my-app && cd my-app
pnpm init                    # 生成 package.json
{
  "name": "my-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

跟 npm init 几乎一样——pnpm 完全兼容 package.json 语义。

添加依赖

pnpm add react react-dom
pnpm add -D typescript @types/react    # devDep
pnpm add -O webpack                    # optionalDep
pnpm add -P axios                       # peerDep(很少手工写)

别名:pnpm i = install、pnpm a = add、pnpm rm = remove、pnpm up = update、pnpm ls = list。

首次 pnpm install 发生了什么

1. 读 package.json 拿依赖树
2. 解析版本范围 → 确定具体版本(写入 pnpm-lock.yaml)
3. 检查 ~/.local/share/pnpm/store 有没有缓存
   - 没有 → 下载 tarball 解压到 store
   - 有   → 跳过
4. 把 store 里的文件硬链接到 node_modules/.pnpm/<pkg>@<ver>/
5. 把 package.json 声明的包 symlink 到 node_modules/
6. 执行 postinstall 钩子

pnpm-lock.yaml

lockfileVersion: '9.0'
settings:
  autoInstallPeers: true
  excludeLinksFromLockfile: false

importers:
  .:
    dependencies:
      react:
        specifier: ^18.3.1
        version: 18.3.1
      react-dom:
        specifier: ^18.3.1
        version: 18.3.1(react@18.3.1)

packages:
  react@18.3.1:
    resolution: { integrity: sha512-xxx... }
    engines: { node: '>=0.10.0' }
  react-dom@18.3.1:
    resolution: { integrity: sha512-xxx... }
    peerDependencies:
      react: ^18.3.1

YAML 格式,人类可读;v9 引入了紧凑的 specifier/version 分离,冲突合并更干净。

lockfile 必须进 git
pnpm-lock.yaml 保证 CI / 队友 / 生产装出一模一样的 node_modules。不提交 = 不可复现。被 .gitignore 掉是事故常见原因。

node_modules 看起来什么样

node_modules/
├── .modules.yaml                  ← pnpm 的元数据
├── .pnpm/
│   ├── react@18.3.1/
│   │   └── node_modules/
│   │       └── react/             ← 真实文件(硬链自 store)
│   ├── react-dom@18.3.1_react@18.3.1/
│   │   └── node_modules/
│   │       ├── react-dom/
│   │       └── react/             ← symlink 到上方 react@18.3.1
│   └── ...
├── react → .pnpm/react@18.3.1/node_modules/react          ← symlink
├── react-dom → .pnpm/react-dom.../node_modules/react-dom  ← symlink
└── (不存在 lodash,即使它是某个子依赖)

几个常用命令

命令说明
pnpm install按 lockfile 装齐(本地用这个)
pnpm install --frozen-lockfileCI 用:lockfile 不对就报错
pnpm install --lockfile-only只更新 lockfile,不装 node_modules
pnpm add <pkg>加依赖到 dependencies
pnpm remove <pkg>删依赖
pnpm update [pkg]按 ^ 规则升版本
pnpm update --latest升到最新 major
pnpm outdated列出可升级的包
pnpm why <pkg>这个包为什么被装了(依赖链)

pnpm dlx:临时执行

# 相当于 npx,下载 create-next-app 并跑
pnpm dlx create-next-app@latest my-app

# 常用别名
pnpm dlx prettier --write .

dlx 把包下到 pnpm 的临时缓存,跑完留着下次用,比每次 npx 重下快很多。

npmrc 配置

# .npmrc(项目根 or ~/.npmrc)
registry=https://registry.npmmirror.com/
auto-install-peers=true
strict-peer-dependencies=false
shamefully-hoist=false
save-exact=true
engine-strict=true
auto-install-peers
自动装 peerDependencies,v8 起默认开——历史 npm 要手动 npm install react 才能用 react-dom。
shamefully-hoist
开了之后,pnpm 会把子依赖也扁平提到 node_modules 顶层——即牺牲严格性换取兼容性。遇到老包或某些打包器才用。
engine-strict
强制 Node 版本符合 engines 字段,不符合安装失败。适合生产严肃项目。

国内镜像

pnpm config set registry https://registry.npmmirror.com/
# 或写到 .npmrc

清缓存

pnpm store prune        # 清 store 里没引用的包
pnpm store path         # 打印 store 位置
rm -rf node_modules && pnpm install    # 重装本项目

本章小结