很多开发者都有过这样的体验:C盘或者项目所在分区莫名其妙就红了,清理软件扫半天也没扫出几个大文件。我自己就在这个问题上栽过好几次跟头,最后花了两周时间把开发机上所有临时目录摸了一遍,才发现真正吞掉磁盘空间的不是安装包,也不是电影资源,而是一堆平时根本不会留意的临时文件:编译器中间产物、包管理器缓存、IDE索引、崩溃转储、日志文件。它们分散在十几个目录里,单个看都不起眼,加起来却能轻松吃掉几十甚至上百GB。
今天想聊的临时文件自动化清理技术,就是围绕这些藏在开发机角落里的缓存与垃圾文件展开的。我会从手动清理的痛点讲起,一步步拆解批处理脚本、计划任务、工程化钩子、磁盘水位触发这几代方案的设计思路和坑点。无论你是被磁盘空间困扰的前端、后端还是移动端开发者,这篇文章应该都能给出一套可落地、可扩展的清理思路,而不是让你继续用"想起来才删"的原始方式硬扛。
1. 临时文件不清理,到底让开发节奏慢在哪
1.1 临时文件在开发机上的真实分布
先别急着写清理脚本,得先搞清楚敌人都在哪。我把自己常用环境里的临时目录梳理了一遍,发现开发机上真正意义上的"临时文件"大体可以分成下面这几类:
| 类别 | 典型位置 | 常见规模 | 清理风险 |
|---|---|---|---|
| 系统级临时文件 | %TEMP%、C:\Windows\Temp、/tmp | 2~10GB | 中,会有文件被占用 |
| 包管理器缓存 | npm cache、pnpm store、pip cache、Maven~/.m2、Go build cache | 5~30GB | 高,清完影响离线构建 |
| 构建方案临时产物 | node_modules/.cache、dist、target/、build/、.next/ | 单项目1~10GB | 中,清完只是构建变慢 |
| 日志与崩溃转储 | IDE crash logs、.dmp文件、%LOCALAPPDATA%\CrashDumps | 1~5GB | 低 |
| IDE/浏览器缓存 | VSCode Cache、JetBrains 索引、Chrome/Edge 的开发者 profile 缓存 | 5~15GB | 高,频繁清导致索引重建 |
这张表只是基准值。如果做过 Android 开发,Gradle 缓存轻松上 10GB;搞游戏开发的同学,Unity 的Library和 ShaderCache 也是几十GB级别;微信开发者工具这类跨端工具,每次调试生成的本地缓存也大得离谱。一个开发机上同时装了 Node、Python、Java、Go 的话,光包管理器的缓存就够写一篇论文了。
1.2 不清理的代价不只是一个盘符变红
很多人觉得磁盘满了大不了删点东西,但这些临时文件对开发节奏的影响远不止"没空间"这么简单。
第一,磁盘满会直接打断构建流程。我遇到过 Docker 拉镜像到一半报no space left on device,构建产物写入失败,测试数据库初始化直接崩。那种时候你根本没法专心写代码,只能临时找地方腾空间。
第二,日志和崩溃转储堆积会误导排查。服务崩了,你想看最近的错误日志,结果发现目录里有几百个.log和.dmp文件,最新日志被淹没在一堆三个月前的转储里。你还得用文件时间排序手动翻,非常浪费时间。
第三,IDE 索引和编辑器缓存大了之后,启动速度明显下降。JetBrains 系 IDEA 的索引缓存如果膨胀到几个GB,每次启动都能听到风扇狂转,内存占用也跟着飙升。很多人以为电脑不行了,其实只是缓存目录早该处理了。
第四,备份体积被无谓拉大。开发机开了系统备份或同步工具的话,日志和缓存目录全会被一起备份,本来 30GB 的镜像能变成 100GB。换句话说,不清理临时文件,你是在为一堆垃圾数据扩充备份成本。
2. 手动清理为什么会越清越乱
2.1 靠记忆做维护本身就不可靠
我见过很多开发者(包括早期的我)清理磁盘的方式就是"凭感觉"。哪边红就点开哪边看看到底什么东西大,然后对着目录名猜一下是不是缓存,是就删。
问题在于,每个工具链的缓存位置和清理方式都不一样。npm 缓存要npm cache clean --force,yarn 和 pnpm 又各有各的 store 管理方法,pip 和 Maven 更是另外一套路径。这些命令记错一次,或者把目录删错一次,代价就是下一轮 install 要全量重新拉包。
更麻烦的是很多临时目录命名毫无规律。我见过 Edge 开发者 profile 下有一串tmp_xxxxxxxx这样的目录,一看就是下载分片或者页面缓存,问题是你不读源码根本不知道它是干嘛的。这时候"手动删除"变成了"赌博删除",删对了省点空间,删错了可能在下次启动某个工具时触发一堆未知错误。
且不说时间成本,手动清理这件事本身会打断心流。写代码写到一半想起来该清缓存了,打开资源管理器逛一圈,选目录、确认大小、删文件,一套流程下来十分钟起步。等你回到代码里,上下文早就凉了。
2.2 手动删除的高风险操作
如果说"凭感觉删"只是效率低,那"全选删"就是实打实的事故制造机。
我印象最深的一次,是团队里一个同事在%TEMP%目录里全选删文件,结果一个正在运行的自动化测试进程还握着其中某个日志文件的句柄。文件倒是被删掉了,但进程后续写入全部失败,测试跑完之后报了一堆莫名其妙的结果,排查了两个小时才怀疑到"日志文件被删了"上面。
pnpm 的全局 store 也是同样的道理。pnpm 依赖是通过硬链接从全局 store 指向项目的,看着node_modules里的文件是按照项目存在,实际上和全局 store 共享底层数据。如果手动把~/Library/pnpm/store或者盘符下的 store 目录删了,所有依赖硬链接都会变成断链,表面上看node_modules目录还在,但一编译就崩,最后只能整个项目重新pnpm install --force。
所以你会发现,手动清理的困境不是"要不要清",而是"怎么清才不出事"。这逼着我开始考虑用脚本把这套操作固化下来,也就是第一代自动化方案。
3. 批处理脚本:第一代自动化的能力边界
3.1 一版合格的 bat/PowerShell 清理脚本长什么样
第一代自动化通常是批处理脚本。我不建议直接上那种全网疯传的"关闭系统服务 + 清理垃圾"一条龙 bat,因为里面很多改动(停服务、改注册表)影响面太大,容易搞崩环境。我自己的做法是让 bat 只做一件事:安全清理临时目录。
下面这个 bat 就是一个比较保守的版本,只动系统和用户的临时目录,并通过内嵌 PowerShell 加了三个保护措施:白名单排除、按时间过滤、错误静默处理。
@echo off setlocal enabledelayedexpansion set "EXCLUDE=.git|node_modules|.pnpm-store|\.cache" for %%D in ("%TEMP%" "%SystemRoot%\Temp") do ( echo [Clean] %%~D if exist "%%~D" ( powershell -NoProfile -Command ^ "$cutoff=(Get-Date).AddDays(-3); Get-ChildItem -LiteralPath '%%~D' -Recurse -Force -ErrorAction SilentlyContinue ^ | Where-Object { $_.LastWriteTime -lt $cutoff -and $_.FullName -notmatch '%EXCLUDE%' } ^ | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" ) ) echo Done.这段代码的意思很清楚:遍历%TEMP%和系统 Temp,只删除三天前修改过的文件/目录,遇到正在占用的文件直接跳过,不会让删除操作中断。但你也看到了,bat 里嵌 PowerShell 转义很痛苦,所以我后来直接改用 PowerShell 脚本,可读性和安全性都更好:
param( [int]$Days = 3, [switch]$WhatIf ) $targets = @($env:TEMP, "$env:SystemRoot\Temp") $excludePattern = 'node_modules|\.git|\.pnpm-store|\.cache' $log = "C:\logs\dev-clean.log" New-Item -ItemType Directory -Force -Path (Split-Path $log) | Out-Null $result = @() foreach ($root in $targets) { if (-not (Test-Path $root)) { continue } $files = Get-ChildItem -LiteralPath $root -Recurse -Force -File -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$Days) -and $_.FullName -notmatch $excludePattern } foreach ($f in $files) { if ($WhatIf) { $result += "DRY: $($f.FullName)" } else { try { Remove-Item -LiteralPath $f.FullName -Force -ErrorAction Stop $result += "OK: $($f.FullName)" } catch { $result += "SKIP: $($f.FullName) - $($_.Exception.Message)" } } } } $result | Out-File -FilePath $log -Append -Encoding utf8 "Total processed: $($result.Count)"这个脚本比 bat 更聪明的地方在于:有用-WhatIf干跑模式,能先输出要删除的文件列表不真删;有日志记录每条删除或跳过原因;用LastWriteTime做时间过滤,避免把刚生成的活跃临时文件误杀。你甚至可以通过参数调保留天数,想保留久一点就传-Days 7。
3.2 接入计划任务,让清理真正"自动化"
脚本写出来只是第一步,要让它自动跑还需要计划任务或者 cron。
Windows 下用schtasks创建每周日凌晨两点的定时清理任务:
schtasks /create /tn "DevTempCleaner" /tr "powershell -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\scripts\dev-clean.ps1 -Days 3" /sc weekly /d SUN /st 02:00macOS 或者 Linux 下面更简单,crontab 跑一下就好:
0 3 * * 0 /usr/local/bin/dev-clean.sh --days 7这里有个很容易踩的坑:在 Windows 上如果给任务指定/ru SYSTEM,脚本在 SYSTEM 账户下运行,访问不了你当前用户的%TEMP%里的文件,因为权限不够。正确做法是让任务以你日常登录的账户运行,或者给任务计划设置"仅在用户登录时运行"。不然你以为每天都清理了,实际什么都没干。
定时清理确实让"自动化"三个字落地了,但它是按日历跑的,不关心磁盘当前是不是真的紧张。可能磁盘已经红了,而计划任务要两天后才执行,这期间如果还要拉镜像、装依赖,只能眼巴巴等。这个能力边界非常明显。
3.3 第一代方案解决不了的问题
批处理脚本加计划任务在思路上是对的,但实际用起来有挺多局限。
第一个问题是脚本完全没有上下文。它不知道当前哪些项目正在活跃开发中,也不知道某个缓存目录是不是构建系统正在高频使用。我一开始把node_modules/.cache加进了清理目录,然后跑了一次,编译时间直接翻倍,因为 Vite 和 Webpack 的增量缓存全没了,所有模块都要重新编译一遍。
第二个问题是规则固定,换个环境就不灵了。在公司内网机器上,很多依赖要经过私有镜像下载,如果你把 npm 缓存整个删掉,下一次 install 又慢又可能因为网络波动失败。在个人笔记本上删缓存无所谓,在离线和代理受限条件下,缓存甚至比磁盘空间更值钱。
第三个问题是缺少反馈闭环。定时脚本跑完,你只看到"删了多少文件",但根本没有量化它到底对磁盘空间、构建时间、启动速度产生了什么影响。没有反馈就不知道怎么调参数,于是脚本变成了一个无脑执行者。
所以我在第一代方案上停了一段时间,然后开始琢磨怎么把"清理"这个动作嵌入到工作流里,让它发生在恰当的时机,而不是固定某个星期天凌晨。
4. 工程化:把清理嵌进开发闭环
4.1 项目级脚本:npm scripts 就是你的清理入口
与其用一个全局脚本去猜哪些缓存该清,不如让每个项目主动暴露自己的清理操作。前端项目最简单的方式就是 npm scripts。
我把清理动作拆成三个层次:
clean:light:只清理临时日志和运行时缓存,比如node_modules/.cache和项目根目录下临时生成的.tmp文件;clean:dist:清理构建产物目录,比如dist、build、.next;clean:all:前面都加上node_modules的完整清理,通常用于排查依赖版本问题时使用。
对应在package.json里长这样:
{ "scripts": { "clean:light": "node scripts/clean-cache.js --level light", "clean:dist": "rimraf dist .next build", "clean:all": "npm run clean:dist && rimraf node_modules && npm install", "predev": "node scripts/clean-cache.js --level light" } }为什么要分成三个层级?因为我发现很多开发者遇到"构建表现怪异"时第一反应就是rm -rf node_modules重装,而实际上 80% 的情况只需要清掉.cache就够了。全量删依赖意味着重新下载、重新编译,成本极高。分三级之后,先试轻量级清理,不行再升级,每次清理的影响范围都可控。
predev这个钩子是我后来加的:每次启动本地开发前,先把上次运行留下的临时文件清一遍。这样即使用户忘了手动清理,只要打开项目就会自动执行一次轻量清扫,非常省心。
4.2 钩子与构建流水线:在正确时机触发
除了 npm scripts,生命周期钩子也能承担清理职责,但必须小心使用。
比如postinstall可以清理安装过程中产生的一些临时文件,这种做法在大型 monorepo 里挺常见。但我强烈建议不要挂在precommit这种高频钩子上做全量清理,因为代码提交应该是轻量操作,跑一次清理脚本可能耗时几十秒,队友会恨死你。
Git 钩子里真正有价值的是post-merge或者post-checkout。分支切换后,依赖版本可能已经变化,这时候执行一次轻量缓存清理,或者根据package-lock.json的变更决定是否需要重新安装,比让开发者手动处理要自然得多。
CI 场景又是另一套玩法。很多人以为流水线里清理缓存就是把所有缓存目录删掉,其实 CI 缓存讲究的是"按 key 淘汰"和"按命中率复用"。比如 GitHub Actions 里用actions/cache缓存 pnpm store,key 是package-lock.json的 hash:
- name: Cache pnpm store uses: actions/cache@v4 with: path: ~/.local/share/pnpm/store key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-pnpm-这种方式比"每轮构建结束后直接把整个缓存目录删掉"高效得多。key 不匹配时旧缓存自动失效,新缓存再写入,完全不用手动触发。换句话说,CI 里的"智能清理"本质上是缓存生命周期管理,而不是大扫除。
4.3 缓存要"可用",不是要"清零"
这一小节是我踩了无数次坑之后最想强调的观点。
我一度追求"极致干净",把 npm cache、Gradle cache、Go build cache 全部清空,结果一个中型项目的冷构建时间从 15 秒变成 8 分钟。更讽刺的是,磁盘空间确实腾出来了,但下一次构建又把缓存装了回来,相当于前面白折腾一场。
清理临时文件的真正目标应该是:让不用的数据离开,保持高价值缓存的可用性。构建缓存命中率比缓存目录体积重要得多。拿 Webpack/Vite 的node_modules/.cache来说,它保存的是编译中间产物和依赖图信息,如果你每天高频开发同一个项目,这个目录应该留着;只有当磁盘空间跌到危险水位,才考虑清掉它。
所以后面我做了一个很现实的分级规则:
- A级缓存(构建缓存、包管理器 store)在有磁盘空间的情况下不动;
- B级日志(IDE 日志、应用日志、崩溃转储)每周清一次,保留 7 天足够;
- C级临时下载文件(安装包分片、缓存视频、临时导出文件)可以按磁盘水位随时清理。
这不是说 A 级永远不清理,而是说要给它一个比"无脑删"更精密的触发条件。这个条件,就是我下一部分要讲的智能策略。
5. 智能化的核心:白名单、阈值与磁盘水位
5.1 从"删所有"到"按规则删"
智能化听起来高大上,落到磁盘清理这件事上,核心就是两个字:规则。早期脚本理解的规则是"这些目录可以删",但真正靠谱的规则应该细化到文件类型、年龄、活跃度和触发条件。
我把规则做成了一份配置文件,用 YAML 描述,脚本启动时读取配置再执行清理。用配置而不是硬编码是为了让不同场景可以独立调参:
rules: - path: "{TEMP}" ageDays: 2 mode: file exclude: ["*.lock", "*.log"] applyWhenDiskFreeBelow: 40 - path: "~/.cache" ageDays: 7 mode: all exclude: ["node_modules", ".git"] applyWhenDiskFreeBelow: 25 - path: "{IDE_CACHE}" ageDays: 14 mode: file requireUntil: "editor-not-running"注意我这里引入了applyWhenDiskFreeBelow字段,这是智能化最关键的转折点。IDE 缓存频繁删会导致索引反复重建,启动反而变慢;日志文件虽然价值低,但多留几天也没什么风险。因此让每一类目录有自己的触发条件,而不是每个星期天统一处决。
还有一个非常容易被忽略的规则是"保留活跃文件"。判断活跃文件的指标是最后写入时间或者最后访问时间。我倾向于用LastWriteTime,因为这个值在 NTFS 上默认可信;Linux 上的 atime 因为性能原因经常被禁止更新,用 atime 做判断会有偏差。
5.2 时间老化与 LRU 策略
配置文件里的ageDays本质上是一种"时间老化策略",但它有局限:同样的目录,里面不同子目录的活跃度不同,一刀切按 7 天删可能误伤还在用的缓存。
所以更精细一点的做法是 LRU(最近最少使用)策略。每次清理前,把目标缓存目录下的所有条目按最后使用时间倒序排列,从最旧的那一头开始删,删到释放出预期的空间为止。伪代码如下:
targetFreePercent = 25 currentFreePercent = GetFreePercent("C:") if currentFreePercent < targetFreePercent: need = (targetFreePercent - currentFreePercent) * diskSize / 100 freed = 0 for cacheDir in activeCacheDirs: entries = listEntries(cacheDir) sortBy(entries, key = lastWriteTime, order = asc) for entry in entries: if freed >= need: break freed += estimateSize(entry) deleteEntry(entry)这套逻辑下,即使同一个目录,内部的子目录也会有不同命运:最近压测发版用的构建产物保留,两个月前临时调试留下来的日志大概率被优先清理。说白了,清理策略从"过期时间"升级到了"按需释放",这才是从定时任务走向智能的关键一步。
5.3 磁盘水位触发与干跑模式
有了 LRU 策略,再配合磁盘水位检查,脚本就可以做到"平时无症状,压力时出手"。
最简单的触发条件是在脚本入口检查当前盘符剩余空间百分比,低于阈值才进入清理流程:
$drive = Get-PSDrive -Name C $total = $drive.Used + $drive.Free $freePercent = [math]::Round($drive.Free / $total * 100, 2) if ($freePercent -lt 20) { & C:\scripts\dev-clean.ps1 -Days 1 -WhatIf }我个人很推荐保留-WhatIf干跑模式至少一周再正式启用。干跑能打出一份"将要删除的文件清单",你拿清单和实际的构建、启动流程对照一下,马上能发现哪些目录的年纪判断是错的。别小看这一步,我见过有人上来就全量执行,结果把某个还在迭代期项目的临时源码目录当垃圾清了。
另外,日志必须保留。清理脚本跑完之后,把每条删除动作以时间 + 文件路径 + 删除结果的格式追加到dev-clean.log。这样万一之后发现项目坏了,你能快速定位是不是清理脚本动了不该动的东西。没有日志的自动清理脚本,等于闭着眼睛拔牙。
5.4 回收站机制与可回滚设计
再往下走一级,就是把"删除"改成"回收站删除"或者"暂存区删除"。
Windows 下用 PowerShell 调用Microsoft.VisualBasic.FileIO.FileSystem可以把文件送入回收站,而不是物理删除:
Add-Type -AssemblyName Microsoft.VisualBasic [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile( $filePath, 'OnlyErrorDialogs', 'SendToRecycleBin' )Linux 上也可以先把文件移动到/tmp/trash,然后定期清理这个目录。这样即使脚本误判,你也还有两三天时间去抢救。
有些激进方案会在清理前生成一个 manifest 文件,记录所有被删文件的位置和大小,删除动作变成"先移走再归档",本质上就是给清理加了一层事务。这样做成本会高一些,但对于正式环境或者多人协作的机器来说,这个可回滚设计值得投入。
我现在的做法是:全局清理脚本默认不直接删除,而是先把目标文件移动到C:\Trash\dev-clean,每三天由操作系统层面的存储感知自动清空一次。相当于我们给临时文件又加了一个"临时停尸房",确认没问题之后再彻底销毁。
6. 踩坑实录:那些用 D 盘教训换来的白名单
6.1 .git 被一次通配符删除带走的灾难
我早期写 bat 脚本时,为了让清理范围更广,直接把某个项目目录下的临时文件夹加进了目标列表。结果某次我为了腾空间,在那个临时文件夹里克隆了一个仓库,里面带着.git目录。清理脚本跑起来之后rd /s /q一路递归删除,.git整个被带走。
等我想起来的时候,仓库已经变成了一堆散落的文件,Git 历史全没了。虽然我用git fsck和 reflog 恢复了一部分对象,但很多分支引用和 stash 记录已经无法找回,那种颗粒无收的挫败感到现在还记得。
从那以后,我所有清理规则的第一条都是硬性排除.git,同时对目标路径做严格校验,不允许任何未经验证的目录进入删除列表。这不是技术问题,是血的教训。
6.2 TEMP 变量被重定义引发的项目源码误删
这个坑比上一个还刁钻。有次脚本执行完之后,整个项目源码少了一大半,我当时第一反应是硬盘坏了,或者中了什么病毒。查了很久才发现,罪魁祸首是%TEMP%环境变量被企业内部安全软件重定向到了D:\Work\TempWorkspace。
在那个目录里,既有正常的临时文件,也有我同事之前放进去的项目副本。我的脚本按照%TEMP%展开的路径执行清理,直接把这个混合区里的项目副本当临时文件删了个痛快。
教训非常深刻:清理脚本绝不能直接信任环境变量展开结果。脚本启动时第一步必须做路径白名单校验,只有确认%TEMP%指向系统默认位置(比如C:\Users\你的用户名\AppData\Local\Temp)才允许继续,否则直接拒绝执行。这段校验代码才十几行,但能挡住最离谱的误操作。
6.3 清空 .cache 后构建变慢,问题不在"删"而在"如何保留"
有段时间我特别执着于把node_modules/.cache清空,觉得这些中间文件迟早会重新生成,留着纯属浪费空间。结果每次清完,下一位同事或者我自己的下一波开发,都要花几分钟甚至十几分钟重新做完整的模块编译。如果你在赶一个紧急需求,这种额外等待能把人急死。
后来我才想明白:这类高价值缓存要优化的不是"删除时机",而是"保留策略"。构建缓存命中率决定构建速度,而缓存保留量与命中率通常正相关。与其无脑删,不如把这个目录设为 A 级受保护目录,除非磁盘空间真的低于安全水位,否则不加入清理目标。
反过来说,日志和崩溃转储则是低价值数据,早删晚删都不心疼,它们才应该是清理脚本的重点打击对象。
6.4 清理时被占用:服务还在写,脚本已经上门
第一次运行自动清理脚本时,我盯着终端看它刷屏,心里还挺爽。结果一个小时后我发现 IDE 里打不开某些文件,排查了半天才发现,清理脚本跑的瞬间,VSCode 正在索引项目,几十个缓存文件被删到一半被系统拒绝,但脚本把-ErrorAction SilentlyContinue一加,错误全被吞了。
你说它没清理到位吧,它确实删了不少;你说它成功吧,大量关联数据被破坏,IDE 缓存状态整个乱了,只好重新构建索引。这让我学到两点:第一,对正在活跃使用的软件缓存目录,最好检测到进程运行时跳过清理;第二,错误信息不能一味静默,至少要把 SKIP 原因写进日志,不然你根本不知道脚本实际执行效果如何。
常见的做法是在删除 IDE 缓存前用 PowerShell 检查进程是否存活,比如Get-Process vscode*、Get-Process idea*,检测到了就跳过这段规则,等下次没有进程占用时再清。看似多了一步,实际上能省掉大量查错时间。
6.5 空目录堆积也是一个隐藏坑
如果脚本只删文件、不管空目录,时间长了会发现临时目录里出现成千上万个空文件夹。Windows 的资源管理器遍历这种目录树时会卡半天,命令行下dir看着密密麻麻全是空壳。
但反过来,无脑把所有空目录全部清空也不对。很多程序把某个空目录当作初始化完成的标记,目录被删了反而要重新生成配置。安全做法是只清理层级超过一定深度的空目录,或者对明确已知的目录执行无递归删除空目录。这部分规则我建议保守,宁可留着也别乱删。
7. 我的维护主张:清理是策略,不是大扫除
如果你问我现在还在用"每周定时大扫除"的方式清理开发机吗?答案是早就不用了。因为我发现真正有效的临时文件自动化清理技术,不应该执着于"删得多干净",而应该专注于"什么时候删、删到什么程度、如何保证可回滚"。
我自己现在的方案是三件套组合:
- 全局清理脚本:用 PowerShell 写成,带路由白名单校验、文件年龄分级、磁盘水位触发、干跑模式、日志输出,通过 Windows 任务计划每周检查一次磁盘状态;
- 项目级入口:每个前端项目都配
clean:light和clean:dist,让清理动作可以在需要时一键触发; - 人工回滚通道:删除目标先进暂存区,三天后自动销毁,期间任何一颗"误删定时炸弹"都有机会被拆除。
这套组合下来,开发机两年来基本没有出现过"磁盘说红就红"的突发状况。每次空间告急,水位检查会提前预警,然后脚本只赶在阈值以下动手,不会把正常缓存当垃圾清。
如果你准备在自己的开发环境落地这套思路,我的建议是先别急着写代码,花半小时把开发机上所有可能产生缓存的目录列一份清单,给它们标上 A/B/C 三个等级,再决定每个等级的保留期和触发条件。清理脚本本身也要纳入版本管理,改一行排除规则都算一次变更记录。毕竟清理逻辑直接影响你每天的工作环境,它比大部分业务代码更需要谨慎对待。