修复清除缓存显示"已释放约0.0MB",与实际清理量不符

现象:设置-清除缓存确认后,无论Cache Storage里实际有多少数据,展示的"已释放约
X MB"几乎总是0.0,即使清理动作本身(caches.delete)确实生效(caches.keys()清理后
变为空数组)。

根因:原实现在caches.delete()前后各调用一次navigator.storage.estimate(),用两次
usage之差算释放量。实测确认Chrome的storage.estimate()不会同步反映Cache Storage的
删除——caches.keys()已经确认清空后,estimate()仍继续报告删除前的usageDetails.caches
数值,实测滞后超过500ms未刷新。人为塞入一个2MB的Cache Storage测试项复现:删除成功
但"已释放"显示0.0MB;塞入3MB项验证修复后正确显示3.0MB。

修法:不再依赖storage.estimate()的前后差值,改成删除前逐个遍历所有Cache
Storage条目、读取每个response的真实blob大小并累加,直接得到本次会话即将删除的
真实字节数,不受浏览器存储统计滞后影响。

验证:npm run build:dev后浏览器实测——(1) 无缓存数据时点击"清除缓存",正确显示
0.0MB且弹层能正常从"清空缓存"态切到"缓存已清理"态、点"完成"可关闭;(2) 用JS人为写入
2MB/3MB测试用Cache Storage条目,分别清理后弹层准确显示2.0MB/3.0MB,且caches.keys()
确认条目已被真实删除。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-06 15:30:40 +08:00
co-authored by Claude Sonnet 5
parent cd092108c2
commit 552c891cc3
+29 -9
View File
@@ -367,20 +367,40 @@ function CacheClear({onToast, onClose}) {
const confirm = async () => {
setBusy(true);
try {
// navigator.storage/caches can be entirely absent (private browsing in some browsers, older
// WebViews) rather than just rejecting -- referencing a missing global throws synchronously,
// which a bare .catch() on the call below can't catch (it never gets that far), so without
// this check the whole confirm() throws, `done` never becomes true, and the "已完成" state is
// never reached even though the button correctly stops spinning via `finally`.
const hasStorageEstimate = typeof navigator !== 'undefined' && navigator.storage && navigator.storage.estimate;
// Was a before/after navigator.storage.estimate() diff around the caches.delete() call --
// measured live (see this round's regression notes): estimate() does NOT reflect a deletion
// synchronously, Chrome keeps reporting the pre-delete `usageDetails.caches` figure for well
// over 500ms after `caches.keys()` already comes back empty. Every real run of this button
// therefore computed `before - after` from two numbers that were still identical, landing on
// "已释放约 0.0 MB" even when megabytes of real thumbnails/temp files were actually deleted --
// confirmed by seeding a 2MB dummy Cache Storage entry and watching this path report 0.0 MB
// freed for it. Summing each entry's real blob size before deleting it sidesteps the OS-level
// storage accounting lag entirely -- the number shown is exactly what this run deleted, not an
// estimate that may or may not have caught up yet.
// caches can be entirely absent (private browsing in some browsers, older WebViews) rather than
// just rejecting -- referencing a missing global throws synchronously, which a bare .catch() on
// the call below can't catch (it never gets that far), so without this check the whole
// confirm() throws, `done` never becomes true, and the "已完成" state is never reached even
// though the button correctly stops spinning via `finally`.
const hasCaches = typeof caches !== 'undefined';
const before = hasStorageEstimate ? (await navigator.storage.estimate().catch(() => ({usage: 0}))).usage || 0 : 0;
let freedBytes = 0;
if (hasCaches) {
const keys = await caches.keys();
for (const key of keys) {
const cache = await caches.open(key);
const requests = await cache.keys();
for (const request of requests) {
const response = await cache.match(request);
if (!response) continue;
// clone() -- match() gives back the one real (non-clonable-twice) body for this entry;
// reading .blob() off it directly would leave nothing for a concurrent reader of the
// same cache, however unlikely that is here.
freedBytes += (await response.clone().blob().catch(() => null))?.size || 0;
}
}
await Promise.all(keys.map(k => caches.delete(k)));
}
const after = hasStorageEstimate ? (await navigator.storage.estimate().catch(() => ({usage: 0}))).usage || 0 : 0;
setFreed(Math.max(0, before - after));
setFreed(freedBytes);
setDone(true);
} catch (err) {
onToast(err.message || '清理失败,请重试');