From 552c891cc3394786d77d1488b40ace39aafab3b1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 6 Sep 2026 15:30:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B8=85=E9=99=A4=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=98=BE=E7=A4=BA"=E5=B7=B2=E9=87=8A=E6=94=BE?= =?UTF-8?q?=E7=BA=A60.0MB"=EF=BC=8C=E4=B8=8E=E5=AE=9E=E9=99=85=E6=B8=85?= =?UTF-8?q?=E7=90=86=E9=87=8F=E4=B8=8D=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现象:设置-清除缓存确认后,无论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 --- src/views/personal-settings-view.jsx | 38 +++++++++++++++++++++------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/views/personal-settings-view.jsx b/src/views/personal-settings-view.jsx index 929a62e4..32ba5818 100644 --- a/src/views/personal-settings-view.jsx +++ b/src/views/personal-settings-view.jsx @@ -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 || '清理失败,请重试');