最近帮一个朋友收拾他的开发机发现系统盘C盘只剩不到5个G整个机器卡得连输入法都掉帧。排查一圈罪魁祸首就是pnpm的缓存仓库目录——那个藏在用户目录下的.local/share/pnpm/store足足吃了30多个G。这种场景我太熟了pnpm装依赖确实快store目录却像个无底洞藏在系统盘里一天天膨胀等到发现的时候往往已经晚了。这篇文章就把修改pnpm缓存仓库目录这件事彻底讲透为什么会膨胀、默认目录在哪、怎么安全地把store和cache迁到别的盘、迁移之后怎么验证、遇到“pnpm下载失败”、“pnpm不是内部或外部命令”这类问题怎么排查。适合所有被系统盘空间逼疯的开发者不管是Windows、macOS还是Linux都能照着操作。1. 为什么非要动这个缓存目录1.1 pnpm的缓存机制和默认路径pnpm和npm/yarn最大的区别在于它采用了一套内容寻址存储机制。简单理解你装过的每个包的每个版本都会被解压后存进一个全局的store目录以文件内容的哈希值命名存放。项目里的node_modules并不是真正复制一份文件而是用硬链接指向store里的文件。这带来的好处是惊人的一百个项目同时用同一个版本的lodash磁盘上只保留一份装依赖速度快到起飞。代价则是store目录会持续累积。你装过的包越多、项目的依赖越多store就越大。我见过一个同时维护多个中大型前端项目的开发者store轻轻松松突破50G。而且pnpm默认把store和cache都放在用户主目录下在Windows上就是C:\Users\用户名\AppData\Local\pnpm-cache和C:\Users\用户名\.local\share\pnpm\store在macOS上是~/Library/Caches/pnpm和~/Library/pnpm/storeLinux上则是~/.local/share/pnpm/store和~/.cache/pnpm。这些路径刚好全在系统盘上。1.2 默认路径带来的四个实际问题第一系统盘空间告急。这是最常见的导火索。现在的依赖动辄几百MB几个大项目下来C盘就红了。Windows系统盘一满各种莫名其妙的报错都会冒出来比如“pnpm下载失败”、“无法写入文件”等等。第二权限问题。公司配的开发机用户目录通常有权限限制或者使用了加密目录、漫游配置store目录在里面写入经常失败。我在帮人排查时就遇到过好多次pnpm install跑到一半突然报错一看日志是store目录写入被拒。第三CI环境缓存失效。很多CI平台的workspace是临时分配的每次跑任务用户目录都是新的如果不把pnpm缓存仓库目录指到持久化挂载盘上每次构建都要把几G的依赖重新下载一遍。有私有仓库的公司带宽再大也扛不住这么耗。第四多盘负载不均衡。很多人的开发机其实有第二块硬盘甚至是一块高速SSD结果所有IO压力全集中在系统盘上另外一块盘闲着吃灰。把store迁到大容量盘上等于把依赖读取的IO压力也一起挪走顺手还能提升一点构建速度。2. 修改前必须搞清楚的几个概念2.1 store、cache、bin三个目录别搞混很多人一上来就把“缓存目录”当成一个东西其实pnpm至少涉及三个目录改错一个都不对。目录作用默认位置以Windows为例修改字段store-dir内容寻址存储存放所有包的解压内容C:\Users\用户名\.local\share\pnpm\storestore-dircache-dir元数据缓存存放registry的包元数据和压缩包C:\Users\用户名\AppData\Local\pnpm\cachecache-dirbin-dirpnpm全局安装的可执行文件目录C:\Users\用户名\AppData\Local\pnpm或.local\share\pnpmglobal-bin-dir我们通常说的“pnpm缓存仓库目录”严格来说指的是store-dir和cache-dir两个。store-dir是空间占用的大头cache-dir虽然小一些但迁移的时候建议一起搬走免得留下一半在那占地方。bin-dir一般不涉及磁盘空间问题但它和环境变量里的PNPM_HOME强相关后面排查问题会提到。这里还要注意node_modules里面那个.pnpm目录叫virtual-store-dir是项目级别的虚拟store不是全局的。它会在每次install的时候从全局store里硬链接过来。你不需要去改它它只是一个项目内的结果目录。2.2 配置优先级谁说了算修改pnpm目录有四种方式命令行参数、环境变量、项目级.npmrc、全局.npmrc。它们的生效优先级从高到低是命令行参数 环境变量 用户级.npmrc 项目级.npmrc pnpm内置默认值。这个优先级顺序是排坑的关键。有时候你明明改了.npmrc运行pnpm config get store-dir一看路径还是老样子那就要先去查环境变量里是不是有PNPM_STORE_DIR或者PNPM_CACHE_DIR它们会把配置文件里的设置盖掉。另外注意pnpm config set store-dir xxx这个命令默认作用于当前项目目录下的.npmrc不是全局配置除非加上--global参数。很多新手在这里踩坑配了半天当前项目生效换个项目又回到默认路径了。2.3 硬链接是迁移安全的关键pnpm的store目录能“搬家”底层依赖的是文件系统硬链接。项目node_modules里的文件和store里的文件是同一个inode修改任意一方另一方都会同步变化。所以理论上把store目录整体移动到新位置之后原来项目的node_modules依然有效不需要重新install。但是注意两个前提第一Windows下硬链接需要NTFS文件系统支持FAT32/exFAT都不行第二跨卷移动store目录时如果原项目没有重新链接新下载的文件和目标盘上的硬链接要在同一分区才能建立。实际操作中我不建议硬搬更稳的是“复制过去再验证”或者干脆让pnpm在新位置重新生成store下面第三部分会给出具体流程。3. 实操把缓存仓库目录迁到指定盘3.1 规划好目标路径先想清楚把store放到哪里。在Windows上我一般建议放在D盘根目录下建一个pnpm文件夹里面分两个子目录D:\pnpm\store和D:\pnpm\cache。Linux/macOS推荐放到/data/pnpm/store和/data/pnpm/cache或者/opt/pnpm/...也可以。路径规划有几点注意不要带中文和空格避免某些工具链在解析路径时出现诡异问题不要放在需要管理员权限才能写入的目录比如C:\Program Files否则pnpm下载依赖时会反复触发权限弹窗如果目标盘是机械硬盘建议再单独建一个目录存pnpm命令输出日志方便排查。3.2 三种配置方式总有一种适合你方式一全局.npmrc修改适合个人开发机这是我最推荐的个人配置方式一劳永逸。打开终端执行# Windows pnpm config set store-dir D:\pnpm\store --global pnpm config set cache-dir D:\pnpm\cache --global # Linux/macOS pnpm config set store-dir /data/pnpm/store --global pnpm config set cache-dir /data/pnpm/cache --global执行完可以用pnpm config list查看当前生效的配置确认store-dir和cache-dir都已经指向新目录。这里--global参数千万别漏漏了就是写到项目里了。方式二项目级.npmrc修改适合团队统一约定如果你希望团队里所有人都用同一个缓存位置可以在项目根目录的.npmrc里写明store-dirD:/pnpm/store cache-dirD:/pnpm/cacheWindows下建议用正斜杠或双反斜杠来写路径避免转义问题。团队协作时可以在git里提交这个文件。不过要提醒一句每个人的实际磁盘路径不一定一样如果你推送了绝对路径别人拉下来可能直接报错所以这种方案更适合大家约定好相同挂载点的情况。方式三环境变量适合CI和临时场景在不方便改文件的环境里环境变量是最灵活的。Windows的cmd或PowerShellsetx PNPM_STORE_DIR D:\pnpm\store setx PNPM_CACHE_DIR D:\pnpm\cacheLinux/macOSexport PNPM_STORE_DIR/data/pnpm/store export PNPM_CACHE_DIR/data/pnpm/cache如果只是某一次安装临时指定可以直接用命令行参数pnpm install --store-dir /tmp/pnpm-store --cache-dir /tmp/pnpm-cache这种临时指定适合CI流水线不太适合日常开发因为没有持久化记忆下次默认还是老路径。3.3 迁移已有缓存直接复制别硬搬改完配置之后旧store目录还在那里几十个G的数据直接删掉太可惜。我尝试过两种迁移方案最后稳定用的是“复制验证切换”。第一步停掉所有正在跑pnpm install的进程确保没有进程占用store目录。Windows下如果有终端开着先全部关掉省得文件锁导致复制中断。第二步把旧store目录复制到新位置。Windows上用robocopy比较稳robocopy C:\Users\用户名\.local\share\pnpm\store D:\pnpm\store /E /COPYALL /DCOPY:DATLinux/macOS直接用cpcp -a ~/.local/share/pnpm/store /data/pnpm/store复制过程中如果中途报错不要慌robocopy可以断点续传再跑一次同样命令就行。第三步复制完成之后先别急着删旧目录。打开终端先验证完整性pnpm config get store-dir pnpm store statuspnpm store status会检查store里的文件是否完整输出一堆文件路径说明校验通过。如果提示有文件缺失或校验失败大概率是复制过程中有文件锁住了重试复制。第四步确认无问题后再删旧目录释放空间。Windows下可以直接删除Linux用rm -rf ~/.local/share/pnpm/store。同样cache目录也按这个流程复制过去。还有个懒人方案如果旧store里大部分包都已经不需要了干脆不复制直接让pnpm在新目录下从零开始构建store。缺点就是首次install会比较慢网络不好的话容易又触发“pnpm下载失败”。所以我个人还是推荐复制一次复制几G半小时搞完省得后续每次install都在那慢慢重新下载。3.4 迁移后的验证步骤配置改了、目录搬了、旧数据删了最后一定要跑一次真实项目安装来验收。我的做法是新建一个临时目录初始化一个最简单的项目npm init -y然后装几个常用依赖mkdir temp-project cd temp-project npm init -y pnpm add lodash安装完成后去新store目录看一眼确认里面按哈希值多了对应的文件夹。再去cache目录看确认元数据缓存也生成在指定位置。这样才算真正迁移成功。最后写一个小脚本或一张备忘录把PNPM_HOME、PNPM_STORE_DIR、PNPM_CACHE_DIR三个环境变量的当前值记下来。以后遇到“配置明明改了但没生效”的问题先对照这个清单排查基本一眼就能看出是哪一层被覆盖了。4. 实操中遇到的坑与排查思路4.1 “pnpm 不是内部或外部命令”和shim报错这个报错太经典了几乎每个pnpm用户都见过。表面是环境变量问题但很多人不知道它和缓存仓库目录修改也有关系。报错细节一般是这样的“pnpm 不是内部或外部命令也不是可运行的程序或批处理文件”或者英文版“pnpm: the global target of the pnpm shim points back at the shim”。原因在于pnpm安装后会创建一个叫pnpm的shim可执行文件放在bin目录里。Windows环境变量PATH里必须包含这个bin目录。很多人改配置时不小心把PNPM_HOME指向了store目录或cache目录那shim文件自然找不到了。解决办法先确认全局bin的真实路径pnpm bin输出会告诉你全局bin在哪Windows一般类似C:\Users\用户名\AppData\Local\pnpm。然后在系统环境变量PATH里加上这个路径注意一行一个路径不要连在一起拼。改完环境变量之后要重新开一个终端已经打开的终端不会重新加载环境变量。如果还报错检查PNPM_HOME这个环境变量本身有没有被设置成奇怪的值比如指向了D:\pnpm\store改成bin路径就行。4.2 “pnpm下载失败”不一定是网络问题热词里反复出现“pnpm下载失败”大多数情况下大家第一反应是换registry源。确实很多人在国内环境直接访问默认源很慢配置淘宝源现在叫npmmirror是有效手段pnpm config set registry https://registry.npmmirror.com但有一种情况容易忽略修改缓存目录之后旧cache目录里如果残留了损坏的tarball或者不完整的元数据pnpm会拿它去安装结果反复报“下载失败”。这种问题重新设registry源也没用。排查思路很简单先清空cache再重试。# 查看cache目录 pnpm cache dir # 强制清除所有缓存 pnpm cache purge清完之后重新installpnpm会把缺失的包重新抓取下来。我遇到的实际案例里十次“下载失败”至少有三次是缓存损坏不是网络问题。另外如果你在离线环境工作迁移store之后一定要保持node_modules里的硬链接有效否则断网状态下连install都跑不动。这里“离线”不涉及任何代理工具指的是公司内网或隔离环境用pnpm install --offline可以强制只从store读取前提是你的store目录已经完整迁移过来了。4.3 workspace报错packages字段缺失pnpm i报ERR_PNPM_INVALID_WORKSPACE_CONFIGURATION packages field missing or empty这个和缓存目录八竿子打不着但热词里出现率高我顺带提一嘴避免有人排查半天方向错了。如果你在项目根目录用了pnpm-workspace.yaml新版pnpm它里面得有子包目录的配置比如packages: - packages/* - apps/*如果这个文件存在但packages字段为空或者你用的是老版本的workspace字段就会碰到这个报错。检查一下yaml字段名是不是拼写错了新版是pnpm-workspace.yaml不是workspace.yaml。这和store目录搬迁没有关系就算不改缓存目录也该报错。4.4 本地私有库链接pnpm link与store的关系有后端同学问“本地项目链接本地私有库必须用pnpm link吗”顺带说清楚。pnpm支持两种本地依赖方式一种是pnpm link把本地包链接到全局store再在项目里链接使用另一种是直接用file:协议指定本地路径。pnpm link这个操作本身不会往store里塞内容它只是建立一个引用。但如果你把store目录搬到别处原来用link建立的引用会失效这是正常现象重新执行一次pnpm link就能恢复。如果你们内部有私有仓库可以把registry源配成公司内部地址配合store的全局缓存依赖安装速度和稳定性会明显提升。4.5 几件事千万不能做最后把我踩过的坑集中说一下。不要在install进行到一半的时候去剪切store目录。文件锁冲突之后整个store可能损坏最好的情况是重新下载最坏的情况是所有项目的node_modules全部失效。正确顺序是把所有终端和编辑器里的构建任务先停掉再操作。不要直接把整个用户目录下的store文件夹“剪切”到另一个盘。Windows下剪切大目录很容易中断中断后源目录和目的地都处于半损坏状态。用robocopy复制验证之后再删原目录才是最稳妥的。不要忽略路径里的特殊字符。某次我把store配到了D:\工作 pnpm\store目录名里有个空格结果某个老项目解析路径时直接报错折腾了一下午最后换成了不带空格的路径。不要图省事只改store-dir不改cache-dir。cache目录确实小很多但它会持续产生读写如果有一个几百G的NAS盘或者HDD盘把cache也挪过去对整个系统IO分布更有利。写在最后根据我个人经验我处理过好几次开发机空间告急的问题无一例外都是pnpm的store目录在作祟其中有两台的C盘空间已经低到连Windows更新都失败。把store和cache迁走之后C盘占用立刻降下来十几个G构建速度不但没变慢反而因为IO压力分散后续跑pnpm install时整体响应还快了一点。最后再分享一个周边小技巧迁完store之后顺手检查一下node_modules里的.pnpm虚拟store如果有项目长期不用可以直接删除整个项目的node_modules硬链接断掉之后store里对应文件也会在下次GC时被清理。pnpm还有个命令是pnpm store prune能清理掉没有任何项目引用的孤儿文件。定期跑一次这两个操作store不会再无声无息长到你认不出来的样子。