From 589640baa7959aa25bd14944ecd8ee00d4bd5455 Mon Sep 17 00:00:00 2001 From: jingrow Date: Sat, 1 Nov 2025 15:14:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96pagetype=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E9=A1=B5=E7=BC=93=E5=AD=98=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/core/pagetype/GenericListPage.vue | 127 ++++++++++++++---- 1 file changed, 103 insertions(+), 24 deletions(-) diff --git a/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue b/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue index 3ace2bf..71b1c69 100644 --- a/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue +++ b/apps/jingrow/frontend/src/core/pagetype/GenericListPage.vue @@ -291,46 +291,125 @@ const cardBadges = computed(() => { const metaFields = ref([]) const linkTitleCache = ref>({}) const pageTypeConfigCache = ref>({}) +const cacheAccessOrder = ref([]) // 记录访问顺序,用于LRU let saveCacheTimer: ReturnType | null = null +const CACHE_EXPIRY_DAYS = 7 // 缓存过期天数 +const MAX_CACHE_SIZE = 500 // 最大缓存条目数 // 从localStorage恢复缓存(避免刷新时抖动) function loadCacheFromStorage() { try { - const cached = localStorage.getItem(`linkTitleCache:${entity.value}`) + const cached = localStorage.getItem('linkTitleCache') if (cached) { const parsed = JSON.parse(cached) - // 合并到当前缓存,但不覆盖已有值 - Object.keys(parsed).forEach(key => { - if (!linkTitleCache.value[key]) { - linkTitleCache.value[key] = parsed[key] - } - }) + const { data, timestamp } = parsed + + // 检查是否过期 + const now = Date.now() + const expiryTime = CACHE_EXPIRY_DAYS * 24 * 60 * 60 * 1000 + if (timestamp && now - timestamp < expiryTime && data) { + // 合并到当前缓存,但不覆盖已有值 + Object.keys(data).forEach(key => { + if (!linkTitleCache.value[key]) { + linkTitleCache.value[key] = data[key] + // 恢复访问顺序 + if (!cacheAccessOrder.value.includes(key)) { + cacheAccessOrder.value.push(key) + } + } + }) + } } } catch (error) { console.error('加载缓存失败:', error) } } -// 保存缓存到localStorage(带防抖,避免频繁写入) +// 更新缓存并记录访问顺序(LRU策略) +function updateCacheWithLRU(key: string, value: string) { + // 如果已存在,更新访问顺序 + if (linkTitleCache.value[key]) { + const index = cacheAccessOrder.value.indexOf(key) + if (index > -1) { + cacheAccessOrder.value.splice(index, 1) + } + } + + // 如果缓存已满,移除最久未使用的 + if (cacheAccessOrder.value.length >= MAX_CACHE_SIZE && !linkTitleCache.value[key]) { + const oldestKey = cacheAccessOrder.value.shift() + if (oldestKey) { + delete linkTitleCache.value[oldestKey] + } + } + + // 添加/更新缓存 + linkTitleCache.value[key] = value + cacheAccessOrder.value.push(key) +} + +// 保存缓存到localStorage(带防抖,页面关闭时立即保存) function saveCacheToStorage() { if (saveCacheTimer) { clearTimeout(saveCacheTimer) } saveCacheTimer = setTimeout(() => { - try { - // 限制缓存大小,只保存最近1000个条目 - const cacheEntries = Object.entries(linkTitleCache.value) - if (cacheEntries.length > 1000) { - // 只保留最新的1000个 - const recentEntries = cacheEntries.slice(-1000) - linkTitleCache.value = Object.fromEntries(recentEntries) + saveCacheImmediately() + }, 500) // 减少防抖时间到500ms,更快响应 +} + +// 立即保存缓存(用于页面关闭时) +function saveCacheImmediately() { + if (saveCacheTimer) { + clearTimeout(saveCacheTimer) + saveCacheTimer = null + } + + try { + // 按访问顺序限制缓存大小 + if (cacheAccessOrder.value.length > MAX_CACHE_SIZE) { + const toRemove = cacheAccessOrder.value.slice(0, cacheAccessOrder.value.length - MAX_CACHE_SIZE) + toRemove.forEach(key => { + delete linkTitleCache.value[key] + }) + cacheAccessOrder.value = cacheAccessOrder.value.slice(-MAX_CACHE_SIZE) + } + + // 保存带时间戳的缓存 + localStorage.setItem('linkTitleCache', JSON.stringify({ + data: linkTitleCache.value, + timestamp: Date.now() + })) + } catch (error) { + // 如果存储空间不足,清理旧缓存 + if (error instanceof DOMException && error.code === 22) { + try { + localStorage.removeItem('linkTitleCache') + // 重试保存,只保存最近100个 + const recentKeys = cacheAccessOrder.value.slice(-100) + const recentCache: Record = {} + recentKeys.forEach(key => { + recentCache[key] = linkTitleCache.value[key] + }) + localStorage.setItem('linkTitleCache', JSON.stringify({ + data: recentCache, + timestamp: Date.now() + })) + linkTitleCache.value = recentCache + cacheAccessOrder.value = recentKeys + } catch (e) { + console.error('保存缓存失败:', e) } - localStorage.setItem(`linkTitleCache:${entity.value}`, JSON.stringify(linkTitleCache.value)) - } catch (error) { + } else { console.error('保存缓存失败:', error) } - saveCacheTimer = null - }, 1000) // 1秒防抖 + } +} + +// 页面关闭时立即保存缓存 +if (typeof window !== 'undefined') { + window.addEventListener('beforeunload', saveCacheImmediately) + window.addEventListener('pagehide', saveCacheImmediately) } async function loadMeta() { @@ -410,7 +489,7 @@ async function preloadLinkTitles() { recordNames.forEach(name => { const cacheKey = `${pagetype}_${name}` if (!linkTitleCache.value[cacheKey]) { - linkTitleCache.value[cacheKey] = name + updateCacheWithLRU(cacheKey, name) } }) return @@ -438,20 +517,20 @@ async function preloadLinkTitles() { withCredentials: true }) - // 缓存结果 + // 缓存结果(使用LRU策略) const records = result.data?.data || [] records.forEach((record: any) => { const name = record.name const titleValue = record[titleField] || name const cacheKey = `${pagetype}_${name}` - linkTitleCache.value[cacheKey] = titleValue + updateCacheWithLRU(cacheKey, titleValue) }) // 对于API未返回的记录,缓存原值(可能是已删除的记录) namesToLoad.forEach(name => { const cacheKey = `${pagetype}_${name}` if (!linkTitleCache.value[cacheKey]) { - linkTitleCache.value[cacheKey] = name + updateCacheWithLRU(cacheKey, name) } }) @@ -463,7 +542,7 @@ async function preloadLinkTitles() { namesToLoad.forEach(name => { const cacheKey = `${pagetype}_${name}` if (!linkTitleCache.value[cacheKey]) { - linkTitleCache.value[cacheKey] = name + updateCacheWithLRU(cacheKey, name) } }) }