diff --git a/apps/music/frontend/src/lib/cache.js b/apps/music/frontend/src/lib/cache.js index dfdb1b9..e9dcf1e 100644 --- a/apps/music/frontend/src/lib/cache.js +++ b/apps/music/frontend/src/lib/cache.js @@ -124,14 +124,29 @@ export async function getCachedBlobUrl(store, attId) { return url } -export async function getAudioUrl(attId) { - const cached = await getCachedBlobUrl(STORE_AUDIO, attId) - return cached || attachmentUrl(attId) +// 短路:内存里已有 blob URL → 同步返回;未启用 cache → 直接网络 URL 不查 IDB; +// 只有启用 cache 且内存没 cache 命中才掏 IDB +export function getAudioUrl(attId) { + if (blobUrlCache.has(attId)) return blobUrlCache.get(attId) + if (!isCacheEnabled()) return attachmentUrl(attId) + // 启用了但内存没缓存:网络立返,后台尝试 IDB 命中后下次会用上 + warmCachedBlob(STORE_AUDIO, attId) + return attachmentUrl(attId) } -export async function getImageUrl(attId) { - const cached = await getCachedBlobUrl(STORE_IMAGE, attId) - return cached || attachmentUrl(attId) +export function getImageUrl(attId) { + if (blobUrlCache.has(attId)) return blobUrlCache.get(attId) + if (!isCacheEnabled()) return attachmentUrl(attId) + warmCachedBlob(STORE_IMAGE, attId) + return attachmentUrl(attId) +} + +function warmCachedBlob(store, attId) { + idbGet(store, attId).then((blob) => { + if (blob && !blobUrlCache.has(attId)) { + blobUrlCache.set(attId, URL.createObjectURL(blob)) + } + }).catch(() => {}) } // ---- 状态 ---- diff --git a/apps/music/frontend/src/sw.js b/apps/music/frontend/src/sw.js index 37f72bb..9a2e523 100644 --- a/apps/music/frontend/src/sw.js +++ b/apps/music/frontend/src/sw.js @@ -20,12 +20,16 @@ self.addEventListener('install', (event) => { event.waitUntil( (async () => { const cache = await caches.open(CACHE) - // 串行避免一次性请求过多 - for (const u of URLS) { - try { - const r = await fetch(u, { cache: 'reload' }) - if (r.ok) await cache.put(u, r) - } catch {} + // 并发 addAll,HTTP/2 多路复用,一次过;失败回退串行 + try { + await cache.addAll(URLS) + } catch { + for (const u of URLS) { + try { + const r = await fetch(u, { cache: 'reload' }) + if (r.ok) await cache.put(u, r) + } catch {} + } } await self.skipWaiting() })(), diff --git a/apps/music/frontend/src/views/PlayerView.vue b/apps/music/frontend/src/views/PlayerView.vue index 8aa9f98..b2b99c6 100644 --- a/apps/music/frontend/src/views/PlayerView.vue +++ b/apps/music/frontend/src/views/PlayerView.vue @@ -773,8 +773,8 @@ async function loadPiece(id) { await nextTick() const first = audioAttachments.value[0] if (first && audioEl.value) { - // 优先用 IDB 缓存的 blob URL,没有再走网络 - audioEl.value.src = await getAudioUrl(first.id) + // 优先用 IDB 缓存的 blob URL(cache 关时同步返回网络 URL,零延迟) + audioEl.value.src = getAudioUrl(first.id) if (wasPlaying) audioEl.value.play().catch(() => {}) } else if (audioEl.value) { audioEl.value.removeAttribute('src')