跳转至

Node 安装与配置

一句话总结:先把 Node.js 本体装好(本机用 zip 绿色版,解压即用),再把 npm / pnpm 的目录、缓存、镜像源全部配到 D 盘。本机所有 Node 相关文件全部收在 D:\APP\Node 下,C 盘不留安装残留。

本文档基于 2026-10-03 重装后全机实测,所有路径均为本机真实路径。

📎 配套文档:《Node 使用与维护》—— 装包命令速查、镜像源配置、维护与清理、注意事项都在那边。

⚠️ 阅读提示:本次是推倒重来式重装(不是升级),目录命名与旧配置完全不同(旧版是 npm-global / node-cache / pnpm\store,新版是 node_global / node_cache / pnpm_store)。如果你手上有其他资料提到旧路径,一律以本文档为准。


一、本机现状总览(2026-10-03 实测)

组件 版本 / 位置 状态
Node.js v24.21.0,D:\APP\Node\node.exe ✅ zip 绿色版,解压即用
npm 11.19.0(随 Node 自带) ✅
pnpm 12.8.1 ✅ 由 npm install -g pnpm 安装
npm 全局目录(prefix) D:\APP\Node\node_global ✅
npm 缓存(cache) D:\APP\Node\node_cache ✅
npm 全局已装 pnpm@12.8.1(npm ls -g 只此一个) ✅
pnpm store(内容仓库) D:\APP\Node\pnpm_store(实际 pnpm_store\v11) ✅
pnpm 全局包目录 D:\APP\Node\pnpm_global(实际 pnpm_global\v11) ✅ 当前为空
pnpm 全局命令目录 D:\APP\Node\pnpm_bin ✅ 当前为空
pnpm 缓存目录 D:\APP\Node\pnpm_cache ✅
pnpm 状态目录 D:\APP\Node\pnpm_state ✅
pnpm 全局已装 无(pnpm ls -g 报 No global packages found) ✅ 符合预期
registry https://mirrors.cloud.tencent.com/npm/(npm 与 pnpm 均已配腾讯云镜像) ✅ 见《Node 使用与维护》六、镜像源配置
PNPM_HOME 未设置 ✅ 新版不需要
PATH 系统变量末尾追加 D:\APP\Node / node_global / pnpm_bin ✅ 见第五节
corepack D:\APP\Node\corepack + corepack.cmd(Node 自带) ✅ 未使用
C 盘残留 仅 AppData\Local\pnpm\config\config.yaml 等配置位,无 pnpm 本体 ✅ 见《Node 使用与维护》7.5

二、Node.js 本体的安装(zip 绿色版)

2.1 为什么用 zip 而不是 msi

方式 特点
官方 .msi 双击安装,全自动;但不能多版本共存、默认装到 C:\Program Files\nodejs
官方 .zip(本机采用) 免安装,解压即用,被称为"便携版/绿色版";路径完全自选、不写系统注册表、卸载=删文件夹
nvm-windows 可多版本切换,前端标配;但多一层管理,本机不需要

本机选择 zip 版,正是为了把 Node 整个放进 D 盘、不碰 C 盘。

2.2 安装步骤

  1. 下载:https://nodejs.org/dist/v24.21.0/node-v24.21.0-win-x64.zip
  2. 解压到 D:\APP\Node,解压后目录里直接能看到 node.exe、npm、npm.cmd、npx 等文件。
  3. 目录名首字母大写 Node 只是为了视觉规范;Windows 路径不区分大小写,纯属个人习惯。
  4. 加环境变量(关键):把 D:\APP\Node 加入 PATH。
  5. ⚠️ 要加的是目录(D:\APP\Node),不是文件(D:\APP\Node\node.exe)——PATH 只认"包含可执行文件的文件夹"。
  6. 验证:新开一个命令行窗口(旧窗口不会刷新环境变量),执行 node -v,能显示版本号即成功。

Node 装的这个文件夹里已经自带 npm,无需单独安装。


三、npm:把全局目录和缓存搬到 D 盘

Node 装好后,npm 默认会把全局包和缓存丢在 C 盘(%AppData%\npm、%LocalAppData%\npm-cache)。要"全部在 D 盘",需改两个配置。

3.1 配置命令

npm config set prefix "D:\APP\Node\node_global"
npm config set cache  "D:\APP\Node\node_cache"

npm config set 只改配置,不会自动建文件夹。若目录不存在,先手动新建 node_global 与 node_cache 两个空文件夹(npm install -g 也会自动创建,但手动建更保险)。

3.2 配置写到哪了

C:\Users\YWH18\.npmrc(用户级 npm 配置),实测内容三行:

1
2
3
prefix=D:\APP\Node\node_global
cache=D:\APP\Node\node_cache
registry=https://mirrors.cloud.tencent.com/npm/

第三行是镜像源,配置方法见《Node 使用与维护》六、镜像源配置。npm 的所有配置(路径 + 镜像)都在这一个文件里。

3.3 验证

1
2
3
npm config get prefix     REM 应输出 D:\APP\Node\node_global
npm config get cache      REM 应输出 D:\APP\Node\node_cache
npm config list

3.4 别忘了把 node_global 加进 PATH

否则 npm install -g 装出来的命令行工具(eslint、vite 等)在任意目录敲不出来。

把 D:\APP\Node\node_global 也加进 PATH(本机加在系统变量里,见第五节)。


四、pnpm:安装与路径配置

4.1 安装

本机采用用 npm 装 pnpm(Windows 上最稳的方式):

npm install -g pnpm

装完验证:

pnpm -v      REM 应输出 12.8.1

关键结果:因为是用 npm 装的,pnpm 本体落在 D:\APP\Node\node_global\node_modules\pnpm\,命令入口是 D:\APP\Node\node_global\pnpm.cmd —— 而不是 pnpm_bin。 这也是 where pnpm 实测输出 D:\APP\Node\node_global\pnpm 的原因(详见 5.3)。

4.2 把 pnpm 的五个目录都指到 D 盘

pnpm 默认同样把东西放 C 盘,需要逐项搬到 D:\APP\Node 下:

1
2
3
4
5
pnpm config set store-dir      "D:\APP\Node\pnpm_store"
pnpm config set global-dir     "D:\APP\Node\pnpm_global"
pnpm config set global-bin-dir "D:\APP\Node\pnpm_bin"
pnpm config set cache-dir      "D:\APP\Node\pnpm_cache"
pnpm config set state-dir      "D:\APP\Node\pnpm_state"
配置项 作用
store-dir 最核心。内容寻址仓库,所有包实体只存这一份,硬链接省磁盘的根基
global-dir 全局安装的包("全局项目")
global-bin-dir 全局命令的 .cmd 入口
cache-dir registry 元数据等缓存
state-dir 运行状态(如更新检查时间)

⚠️ pnpm 不读 .npmrc 里的这些路径项,必须用 pnpm config set 单独写进 pnpm 自己的配置文件。

4.3 配置写到哪了(pnpm 用了两个文件)

实测落在 pnpm 自己的默认配置目录 C:\Users\YWH18\AppData\Local\pnpm\config\,分两个文件:

① config.yaml —— 管五个路径(169 字节)

1
2
3
4
5
cacheDir: D:\APP\Node\pnpm_cache
globalBinDir: D:\APP\Node\pnpm_bin
globalDir: D:\APP\Node\pnpm_global
stateDir: D:\APP\Node\pnpm_state
storeDir: D:\APP\Node\pnpm_store

② auth.ini —— 管镜像源(48 字节)

registry=https://mirrors.cloud.tencent.com/npm/

⚠️ pnpm 12 把"路径"和"镜像源"拆到了两个文件里,不是配置丢了一半。用 pnpm config set registry ... 改的镜像源进的是 auth.ini,用 pnpm config set store-dir ... 改的路径进的是 config.yaml。 这两个文件都只是几百字节的文本配置,里面没有 exe、没有 node_modules,不是安装目录,可以放心留着。

4.4 验证

1
2
3
4
5
6
pnpm config list          REM 五个路径都应是 D:\APP\Node\...,registry 应为腾讯云
pnpm config get registry  REM 应输出 https://mirrors.cloud.tencent.com/npm/
pnpm root -g              REM 应输出 D:\APP\Node\pnpm_global\v11
pnpm bin -g               REM 应输出 D:\APP\Node\pnpm_bin
pnpm store path           REM 应输出 D:\APP\Node\pnpm_store\v11
pnpm store status         REM 应显示 Packages in the store are untouched

pnpm root -g 末尾那个 v11 是正常的。pnpm 按 Node 主版本隔离全局包,子目录名与 Node 版本相关,不是错误。

4.5 本机不需要 PNPM_HOME

实测 PNPM_HOME 未设置,也不影响使用——因为用的是 global-bin-dir 直接指定路径的方式,不依赖 PNPM_HOME。旧文档里那套 PNPM_HOME 是官方 install.ps1 脚本的机制,本机本次没走那条路。


五、环境变量与 PATH

5.1 需要进 PATH 的三个目录

目录 作用
D:\APP\Node 让 node / npm / npx / corepack 全局可用
D:\APP\Node\node_global 让 npm 全局装的工具可用(pnpm 命令本体也在这里)
D:\APP\Node\pnpm_bin 让 pnpm 全局装的工具可用(当前为空,为将来准备)

5.2 本机 PATH 实测

系统 PATH(HKLM) 末尾三项为本机新增:

1
2
3
4
...(前面是 Python / anaconda / Git / VS Code 等既有项)...
D:\APP\Node
D:\APP\Node\node_global
D:\APP\Node\pnpm_bin

用户 PATH(HKCU) 与 Node 无关,共 5 项:

1
2
3
4
5
C:\Users\YWH18\.local\bin
C:\Users\YWH18\AppData\Local\Microsoft\WindowsApps
C:\Users\YWH18\.dotnet\tools
D:\AI\ClaudeCode
D:\CPPenv\mingw64\bin

用户 PATH 类型是 REG_SZ(不含 %变量% 引用),本机没有 %PNPM_HOME% 这类展开变量需要保护,改起来没有旧文档里那些坑。

5.3 命令查找顺序(一个容易困惑的点)

系统 PATH 里 node_global 排在 pnpm_bin 之前,加上 pnpm_bin 目前是空的,所以:

1
2
3
where pnpm
REM D:\APP\Node\node_global\pnpm
REM D:\APP\Node\node_global\pnpm.cmd

命中的是 node_global 里的 pnpm(因为它是 npm 装的)——这是正确现象,不是配置跑偏。

同名命令的优先级:node_global(npm 系) > pnpm_bin(pnpm 系)。

5.4 改 PATH 的正确姿势(改前先备份)

REM 备份:改坏了两双击这个 .reg 文件即可还原
reg export "HKCU\Environment" "%USERPROFILE%\Desktop\env-backup.reg" /y
  • 推荐 GUI:Win+R → sysdm.cpl → 高级 → 环境变量 → 选中 Path → 编辑。
  • 命令行(若走命令,必须读未展开的原始值,否则 %VAR% 会被展开成死字符串):
1
2
3
4
$raw = (Get-Item 'HKCU:\Environment').GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
$seen = [System.Collections.Generic.HashSet[string]]::new()
$clean = ($raw -split ';' | Where-Object { $_ -ne '' -and $seen.Add($_) }) -join ';'
$clean

两个坑别踩:

  1. 不要用 [Environment]::SetEnvironmentVariable —— 它会把 %VAR% 展开成字面量,并把类型降级为 REG_SZ;
  2. 不要用 Get-ItemProperty ... .Path 读值 —— 它默认展开变量,拿去写回等于把变量引用丢掉。

六、D 盘八个文件夹分别干什么

这一节回答三个问题:每个文件夹是干什么的、它的配置写在哪、这项配置起什么作用。

6.1 一张表看懂

文件夹 作用(这个文件夹管什么) 对应配置项 配置写在哪 这项配置的作用
node_cache npm 的下载缓存。从 registry 下载的包 tarball、元数据都缓在这,下次装同一版本直接用缓存不重下 cache C:\Users\YWH18\.npmrc 告诉 npm "缓存放 D 盘",避免默认写 C 盘 %LocalAppData%\npm-cache
node_global npm 的全局安装目录。npm i -g 装的包和命令入口(.cmd)都落这。本机 pnpm 本体也在这 prefix C:\Users\YWH18\.npmrc 告诉 npm "全局包装 D 盘",且必须在 PATH 里,否则全局命令敲不出来
node_modules Node 自带的模块目录。放 npm 和 corepack 自己的实现代码,不是用户装的包,不要手动动它 无 — Node 安装包自带,无对应配置项
pnpm_bin pnpm 全局命令入口目录。pnpm add -g <工具> 生成的 .cmd 放这 global-bin-dir ...\AppData\Local\pnpm\config\config.yaml 指定全局命令放 D 盘,且必须在 PATH 里,否则 pnpm add -g 装的工具无法直接调用
pnpm_cache pnpm 的缓存目录。存 registry 元数据、pnpx/pnpm dlx 的临时包等 cache-dir 同上 config.yaml 把 pnpm 的缓存也赶到 D 盘,不占 C 盘
pnpm_global pnpm 的全局包目录。全局安装的"全局项目"记录放这,pnpm root -g 指向它 global-dir 同上 config.yaml 指定全局包实体与记录的落点
pnpm_state pnpm 的运行状态目录。存更新检查时间戳等状态信息 state-dir 同上 config.yaml 把状态文件放 D 盘
pnpm_store pnpm 内容寻址仓库(最核心)。所有包实体全盘只存这一份,各项目 node_modules 里是硬链接指过来 store-dir 同上 config.yaml pnpm 省磁盘的根基;不同项目复用同一份包,只占一份空间

📌 表里的 node_global 和 pnpm_bin 是"bin 目录",概念最容易懵。下一节用一个真实例子把它讲透——为什么必须有、为什么必须进 PATH、npm 和 pnpm 在这里有什么本质差别。

6.2 深入理解:bin 目录(node_global / pnpm_bin)到底是什么

这两个目录最容易懵。一句话先记住:bin 目录是"全局命令的门面",只放快捷方式(.cmd),不放包的代码。

6.2.1 为什么必须要有这么一个目录

核心矛盾:你敲的 cowsay、vite 这些命令名,本质是"文件名"。Windows 要找到它,只能做一件事——把 PATH 里列出的每个目录挨个翻一遍,看有没有叫这个名字的文件。

所以一个全局工具要能"在任何目录敲出来",就必须有一个以命令名命名的文件,躺在 PATH 里的某个目录下。

但包的代码有几百上千个文件(index.js、package.json、dist\……),全塞进 PATH 目录显然不行。于是包管理器把它们分成两半:

        ┌──────────────────────────┐
        │  pnpm add -g cowsay      │
        └───────────┬──────────────┘
                    │
        ┌───────────┴────────────┐
        ▼                        ▼
┌───────────────────┐   ┌──────────────────┐
│  包的全部代码      │   │  一个叫 cowsay    │
│  (实体,几百个文件)│   │  的 .cmd 快捷方式  │
└─────────┬─────────┘   └────────┬─────────┘
          ▼                      ▼
   pnpm_global\v11\...      pnpm_bin\cowsay.cmd
   (仓库,不进 PATH)       (门面,必须进 PATH)

pnpm_bin 就是右边那个"门面" —— 里面只有几个几十字节的 .cmd 文件。

6.2.2 快捷方式(shim)长什么样

pnpm_bin 现在是空的,但 node_global 里有一模一样的东西可以对照。本机 D:\APP\Node\node_global\pnpm.cmd 的实际内容:

1
2
3
4
5
6
7
8
9
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
"%dp0%\node_modules\pnpm\pnpm.exe"   %*

这一整坨,本质只干一件事(看最后一行):把你敲的 pnpm 后面的参数,原封不动转交给 node_modules\pnpm\pnpm.exe 去执行。

它自己不干活,只是"代收点"。这就是所谓的 shim(垫片 / 转发器)。

bin 目录 现在里面有什么 里面是什么
node_global pnpm.cmd、pn.cmd、pnpx.cmd… 转发给真身的 shim
pnpm_bin (空)将来会长出 cowsay.cmd、vite.cmd… 同上

6.2.3 npm 与 pnpm 的关键差别:合并 vs 分家

这是最容易看漏的一点:

1
2
3
4
5
npm:  入口 node_global\pnpm.cmd              ┐
       本体 node_global\node_modules\pnpm\    ┘  ← 同一个目录

pnpm: 入口 pnpm_bin\xxx.cmd                  ┐
       本体 pnpm_global\v11\...               ┘  ← 两个不同目录
  • npm 是"一个目录全包":node_global 里既有 .cmd 快捷方式,又有 node_modules 放代码;
  • pnpm 是"前店后仓":pnpm_bin 只放快捷方式,pnpm_global 放代码。

所以更准的说法不是"npm 装在它自己的路径、pnpm 装在 pnpm_bin",而是——pnpm 比 npm 多拆了一层,把入口单独拎出来放 pnpm_bin 了。

6.2.4 规律:「谁装的,落谁的地盘」

决定命令入口去哪里的,不是"装的是什么包",而是"你用的是哪个工具去装的"。

安装方式 入口(你敲的命令) 本体(包的代码)
npm i -g xxx node_global\xxx.cmd node_global\node_modules\xxx\
pnpm add -g xxx pnpm_bin\xxx.cmd pnpm_global\v11\...\node_modules\xxx\

同一个包、两种装法,落点完全不同:

npm  i   -g cowsay     REM 入口 → node_global\cowsay.cmd   本体 → node_global\node_modules\cowsay\
pnpm add -g cowsay     REM 入口 → pnpm_bin\cowsay.cmd      本体 → pnpm_global\v11\...\cowsay\

这解释了本机一个看起来矛盾的现象:where pnpm 命中的是 node_global\pnpm.cmd,而不是 pnpm_bin。原因是 —— pnpm 本体这个包当初是用 npm install -g pnpm 装的,按"谁装的落谁的地盘",它自然落在 node_global,跟"pnpm 这个工具自己"没关系。这是正确现象,不是配置跑偏。

6.2.5 唯一的"生死线":bin 目录必须在 PATH 里

pnpm_bin 目前是空的,这是正常的 —— 它只在第一次 pnpm add -g <工具> 之后才会长出东西。它是"预留好的货架"。

但它必须在 PATH 里,否则会出这种怪事:

pnpm add -g cowsay     REM 装成功了,pnpm_global 和 pnpm_bin 里文件都生成了
cowsay "hi"            REM ❌ 'cowsay' 不是内部或外部命令

包明明装好了、文件明明在那儿,敲命令却找不到 —— 因为 Windows 翻 PATH 时压根没翻到 pnpm_bin 这个目录。这类"装了却用不了"的玄学问题,九成都是 PATH 漏了 bin 目录。

node_global 同理、也必须进 PATH,否则 npm i -g 装的东西一样敲不出来。两者是同一个机制的两个实例。

6.3 实测当前内容

文件夹 现在里面有什么
node_cache _cacache(缓存实体)、_logs、_update-notifier-last-checked
node_global node_modules\pnpm(pnpm 本体)、pn / pnpm / pnpx / pnx 各三件套(无扩展名 + .cmd + .ps1)
node_modules npm、corepack
pnpm_bin 空(还没装过 pnpm 全局工具)
pnpm_cache lockfile-verified.jsonl、v11\(metadata 等)
pnpm_global 空(还没装过 pnpm 全局包)
pnpm_state pnpm-state.json
pnpm_store v11\

6.4 配置文件都在哪(全机只有三个文件)

配置文件 里面写了什么 管谁
C:\Users\YWH18\.npmrc prefix / cache / registry 三行 npm(pnpm 也会顺带读 registry)
C:\Users\YWH18\AppData\Local\pnpm\config\config.yaml cacheDir / globalBinDir / globalDir / stateDir / storeDir 五个路径 pnpm 的目录归属
C:\Users\YWH18\AppData\Local\pnpm\config\auth.ini registry=https://mirrors.cloud.tencent.com/npm/ 一行 pnpm 的镜像源

⚠️ 注意 pnpm 用了两个文件:config.yaml 管路径,auth.ini 管镜像源。这是 pnpm 12 的拆分方式,不是配漏了。 AppData\Local\pnpm\config\ 整个目录只有几百字节文本、无 exe、无 node_modules,是配置位不是安装位,可放心保留。

6.5 store 布局说明

pnpm_store\v11 下实测只有 files + index.db 两项,没有 links / projects —— 这是 pnpm 12 全局虚拟存储(GVS)的新布局,属正常,不是损坏。

📌 这个 store 到底省了多少磁盘、什么情况下不省(跨盘符)、为什么资源管理器看不出省了 —— 详见 7.3~7.6,那里有本机实测数据。


七、装包时磁盘上发生了什么

7.1 npm 的包去向

场景 包内容 命令入口
项目安装 npm install 项目 node_modules\ 项目 node_modules\.bin\
全局安装 npm i -g <包> D:\APP\Node\node_global\node_modules\ D:\APP\Node\node_global\<命令>.cmd
下载缓存 D:\APP\Node\node_cache\_cacache\ —

7.2 pnpm 的包去向

场景 包内容 命令入口
项目安装 pnpm install 实体进 D:\APP\Node\pnpm_store\v11\(全局唯一一份),项目 node_modules\ 里是硬链接 项目 node_modules\.bin\
全局安装 pnpm add -g <包> 实体进 store,全局项目记录在 pnpm_global\v11\ D:\APP\Node\pnpm_bin\<命令>.cmd
临时执行 pnpx <包> 走全局虚拟存储 / cache-dir 下的临时缓存 临时,不落 pnpm_bin

一句话:npm 全局的包"实打实"放在 node_global 里;pnpm 的包全部集中在 pnpm_store 里,别处只有链接。

7.3 pnpm 省磁盘的真相(本机实测)

一句话:省的是"后续同盘项目的增量",不是"第一份数据"。

pnpm 把包实体集中放在 pnpm_store,项目里的 node_modules 通过硬链接指过去。硬链接的特点是:多条路径指向同一份数据,磁盘上只存一份,每加一条链接几乎不增加占用。

本机实测(2026-10-03,两个项目装了完全相同的 @tensorflow/tfjs-node 依赖):

位置 逻辑大小 其中独占(真吃盘) 其中共享(增量≈0)
pnpm_store 262.4 MB 6.5 MB 255.9 MB
D:\11\node_modules 274.7 MB 3.2 MB 271.5 MB
D:\22\node_modules 274.7 MB 3.2 MB 271.5 MB
C:\...\test-pnpm\node_modules 274.7 MB 274.7 MB 0 MB

按物理身份去重后统计(同一份数据无论多少链接只算一次):

组合 逻辑大小 真实物理占用 省下
D 盘:store + D:\11 + D:\22 811.8 MB 268.7 MB 543.1 MB(约 67%)
C 盘:test-pnpm 单独一个 274.7 MB 274.7 MB 0 MB

怎么读这两张表:

  • D:\11 和 D:\22 这两个项目文件夹本身几乎不占额外空间 —— 每个只有 ~3.2 MB 是真正属于自己的(锁文件、.modules.yaml 等),剩下 271.5 MB 全是硬链接,指向 store,不额外吃盘。
  • 但数据不是凭空消失了,它总得存在一个地方 —— 存在 pnpm_store 里,那 262.4 MB 是实打实的数据实体。
  • 所以整体账是:store + N 个同盘项目 ≈ 1 份的大小。

⚠️ 最常见的误解:以为"pnpm 让每个项目都很小"。准确说法是——不是"N 个项目各占一点点",而是"N 个项目 + store 加起来约等于 1 份"。第一份数据要付全款(进 store),后面每个同盘项目增量≈0。

两个诚实的补充:

  1. 共享是有条件的:上面的漂亮数字,是因为 D:\11 和 D:\22 装了完全相同的依赖。依赖不同时,各自独有的包仍需各占一份,共享率会下降;但公共部分(lodash、typescript 这类高频包)永远共享,整体仍然省。
  2. 未计目录元数据:NTFS 中每个目录本身也要占几百字节到几 KB。pnpm 的 .pnpm 结构很深、目录很多,这部分不在上表统计内,实际总量会比 268.7 MB 略高,不影响结论。

7.4 跨盘符:硬链接失效,自动退化为"复制"

这是本机最值得记住的一条硬性边界。

NTFS 的硬链接不能跨卷(不能跨盘符)。所以如果 store 在 D 盘、项目在 C 盘,硬链接根本建不起来。pnpm 早就知道这点,会自动降级成真实复制——而且它在输出里明说了,只是字眼不同:

1
2
3
C 盘项目:  Packages are COPIED      from the content-addressable store to the virtual store.
D 盘项目:  Packages are HARD LINKED from the content-addressable store to the virtual store.
                  ↑↑↑↑↑↑↑↑↑↑↑ 一个词之差,天壤之别

本机实测证据(抽样 store 中的文件,看它们被谁引用):

抽样检查 结果
store 文件被 D:\11 项目引用 21 / 40
store 文件被 D:\22 项目引用 21 / 40
store 文件被 C 盘 test-pnpm 引用 0 / 40 ← 关键
C 盘项目文件中被复用的 0 / 15(全是独占副本)
D 盘项目文件中被复用的 12 / 15(硬链接共享)

结论:

  • 同盘(store 与项目都在 D 盘)→ 硬链接 → 省空间(实测省 543.1 MB)
  • 跨盘(store 在 D、项目在 C)→ 退化为复制 → 一点不省,C 盘那个项目实打实多占了 274.7 MB

补充:pnpm 有配置项 package-import-method,可选 auto(默认,能链就链)/ hardlink / copy / clone。不需要手动去改 —— 默认的 auto 判断就是对的(同盘建链、跨盘复制)。

7.5 为什么资源管理器显示的大小不准

用右键属性看 D:\11、D:\22、C 盘那个项目,三个显示的体积会几乎一模一样,但这不代表它们都占了那么多空间。

原因:资源管理器显示的是「逻辑大小」,不是「磁盘实际占用」。

  • 资源管理器统计的是"目录里每个文件条目记录的大小之和";
  • 而硬链接的每一条链接,在目录里都是一个独立条目,每个条目都老实写着"我这个文件 100KB";
  • 所以 3 条链接指向同一份数据、磁盘上只存了 1 份,资源管理器还是按 3 份算给你看。
1
2
3
4
                    资源管理器显示      磁盘真实占用
D:\11\node_modules      ~274 MB       ≈ 3.2 MB(其余全是链接)
D:\22\node_modules      ~274 MB       ≈ 3.2 MB(同上,跟 11 共用同一份)
C:\...\test-pnpm        ~274 MB       ≈ 274 MB(真实复制,真占)

⚠️ 连资源管理器右边那列「占用空间 / 磁盘上的大小」对硬链接同样不准 —— 它也会把每条链接按簇对齐后累加,硬链接照样被重复计算。别用资源管理器判断 pnpm 省没省空间。

想看到真相,用这几招:

1
2
3
4
5
6
7
8
9
REM ① 看某个文件有几条链接、都被谁引用(最直接,铁证)
fsutil hardlink list "D:\11\node_modules\.pnpm\has-flag@4.0.0\node_modules\has-flag\index.d.ts"

REM ② 看磁盘真实剩余空间
fsutil volume diskfree D:

REM ③ 最直观的实测
REM    删掉 D:\11,刷新看 D 盘剩余空间 → 几乎不变(数据还在 store 和 D:\22 手里)
REM    再删掉 C 盘那个 test-pnpm → C 盘立刻回收约 274 MB

第 ① 招的输出长这样(三条路径、一份数据,且不含 C 盘项目):

1
2
3
\11\node_modules\.pnpm\has-flag@4.0.0\node_modules\has-flag\index.d.ts
\22\node_modules\.pnpm\has-flag@4.0.0\node_modules\has-flag\index.d.ts
\APP\Node\pnpm_store\v11\files\00\1bd318b4...

7.6 纪律:Node 项目一律建在 D 盘

由 7.3~7.5 直接推出一条硬纪律:

不只是"store 要放 D 盘",项目本身也必须建在 D 盘。

理由:

  • 硬链接不能跨卷 → 项目建在 C 盘,pnpm 最大的卖点(省磁盘)直接归零;
  • 建在 D 盘,第二个起每个项目增量≈0;建在 C 盘,每个项目都是完整一份,白占几百 MB。

自查方法(任何时候想知道某个项目省没省):

1
2
3
4
5
REM 看 pnpm 安装时的措辞,出现 COPIED 就说明跨盘了、没省
pnpm install

REM 或直接查某个文件被谁链接,不出现在 store 引用列表里就是独立副本
fsutil hardlink list "<项目>\node_modules\.pnpm\<包>\node_modules\<包>\package.json"

八、npm / npx / pnpm / pnpx 的关系

命令 归属 是什么 典型用途
npm npm 自带 包管理器 安装/卸载/管理包
npx npm 自带 临时执行器 npx <包名> 临时下载并运行,用完即走
pnpm 独立安装 包管理器 与 npm 同类的装包工具(更快、更省盘)
pnpx pnpm 自带 临时执行器 pnpm 版 npx,等价于 pnpm dlx
  • npm ↔ npx 是一对;pnpm ↔ pnpx 是同样的一对。
  • npm 与 pnpm 功能对等:都能装包、跑脚本、管依赖;pnpm 在速度、磁盘、依赖隔离上更优,命令习惯几乎兼容。
  • 两者共用同一套 Node 运行时,但配置各写各的(改镜像源要两处都改,见《Node 使用与维护》六、镜像源配置)。

九、pnpm 12 与旧版的差异(了解即可)

pnpm 12 的核心引擎用 Rust 重写,命令、配置项、锁文件格式保持兼容,但几处形态变了:

项目 pnpm 11 及更早 pnpm 12(现)
全局包名 @pnpm/exe pnpm(无作用域)
store 布局 含 links / projects 只有 files + index.db(GVS 新布局)
安装方式 官方 install.ps1 本机改用 npm install -g pnpm
corepack 可用 corepack 管理 官方已移除 corepack 安装推荐
pnpx 缓存 在 store 内 走 cache-dir(本机已指到 D 盘)

其他更严格的默认值(了解即可):

  • Git 依赖强制 HTTPS:所有 GitHub/GitLab 依赖走 HTTPS,不再探测本机 SSH 配置。
  • 未知配置项直接报错:pnpm-workspace.yaml 里写了不认识的配置项会直接报错,不再静默忽略。
  • 项目感知全局二进制:全局装的 node/deno/bun 会自动跟随当前项目锁定的版本。
  • 禁止自举:pnpm add -g pnpm 会被拦截,报 ERR_PNPM_GLOBAL_PNPM_INSTALL,提示改用 pnpm self-update。

附:环境体检命令(一键检查)

命令块可整体多行复制,粘贴到 CMD 运行。结果与"期望"对照,全绿即健康。

REM ===== 一、版本与命令位置 =====
node --version
npm --version
pnpm --version
where node
where npm
where pnpm

REM ===== 二、npm 配置 =====
npm config get prefix
npm config get cache
npm config get registry
type "C:\Users\YWH18\.npmrc"
npm ls -g --depth=0

REM ===== 三、pnpm 配置 =====
pnpm config list
pnpm config get registry
pnpm root -g
pnpm bin -g
pnpm store path
pnpm store status
pnpm ls -g

REM ===== 四、环境变量 =====
reg query "HKCU\Environment" /v Path
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path

REM ===== 五、目录结构 =====
dir /b "D:\APP\Node"
dir /b "D:\APP\Node\node_global"
dir /b "D:\APP\Node\node_cache"
dir /b "D:\APP\Node\pnpm_bin"
dir /b "D:\APP\Node\pnpm_store\v11"
dir /b "D:\APP\Node\pnpm_global"

REM ===== 六、C 盘残留检查 =====
dir /a "C:\Users\YWH18\AppData\Local\pnpm"
where /r C:\Users\YWH18 pnpm.exe 2>nul
if exist "C:\Users\YWH18\AppData\Roaming\npm" (echo [警告] C盘存在 npm 默认全局目录) else (echo [正常] C盘无 npm 默认全局目录)
if exist "C:\Program Files\nodejs" (echo [警告] C盘存在 nodejs 安装目录) else (echo [正常] C盘无 nodejs 安装目录)

REM ===== 七、镜像连通 =====
npm ping
pnpm ping

期望值对照:

段 期望
一 node v24.21.0 / npm 11.19.0 / pnpm 12.8.1;node、npm 指向 D:\APP\Node;pnpm 指向 D:\APP\Node\node_global
二 prefix=D:\APP\Node\node_global、cache=D:\APP\Node\node_cache、registry=https://mirrors.cloud.tencent.com/npm/;npm ls -g 至少含 pnpm@12.8.1
三 五个路径全在 D:\APP\Node 下;registry 与 npm 一致(腾讯云);root -g=pnpm_global\v11;bin -g=pnpm_bin;store path=pnpm_store\v11;store status="untouched"
四 系统 Path 末尾含 D:\APP\Node / node_global / pnpm_bin;用户 Path 无重复项
五 node_global 有 pnpm 系列命令 + node_modules;pnpm_store\v11 只有 files + index.db;pnpm_bin / pnpm_global 可能为空
六 AppData\Local\pnpm 只有 config\;where /r 找不到 C 盘 pnpm.exe;两条均为 [正常]
七 均返回 PONG(腾讯云镜像实测 npm 约 500ms)