chore(图片缓存系统): 添加更多执行流程的注释和错误log输出,并删除无用代码

This commit is contained in:
hzj 2025-12-26 19:22:24 +08:00
parent 8a5f158724
commit 05d142b9db
4 changed files with 121 additions and 139 deletions

View File

@ -99,7 +99,7 @@ async function processImage(el, url, skipFade = false) {
await nextTick()
try {
// 统一使用缓存管理器处理所有可缓存资源
// 目标符合缓存条件
if (imageCacheManager.isBuildAsset(url)) {
// 获取缓存的图片 URL如果是缓存的图片会返回 blob URL否则返回原始 URL
const imageUrl = await imageCacheManager.getCacheImageUrl(url)

View File

@ -15,8 +15,10 @@ import { useLangStore } from '@/stores/lang' // 移动到顶部
// 初始化环境信息
logEnvInfo()
// 版本检查后添加
await versionChecker.checkVersion()
// 检查版本更新(首先判断执行一次对缓存的操作,最后返回检测结果)
if (await versionChecker.checkVersion()) {
console.log('版本已更新')
}
// 初始化权限系统
console.debug('🔐 Permission system initialized')
@ -92,7 +94,7 @@ app.use(i18n)
app.mount('#app')
// 定时检查逻辑
// 定时检查是否更新
if (versionChecker.enableCheck) {
setInterval(async () => {
const versionResult = await versionChecker.checkServerVersion()

View File

@ -6,7 +6,7 @@ class ImageCacheManager {
this.cacheVersionKey = 'h5_app_version'
this.currentAppVersion = import.meta.env.VITE_APP_VERSION || '1.0.0'
this.currentImageVersion = IMAGE_CACHE_VERSION.version // 使用完整版本号
this.versionType = import.meta.env.VITE_VERSION_TYPE || 'patch' // major, minor, patch
// 移除了 this.versionType = import.meta.env.VITE_VERSION_TYPE || 'patch'
this.imageCacheName = `likei-images-v${this.currentImageVersion}` // OSS图片
this.assetCacheName = `likei-assets-v${this.currentImageVersion}` // 本地assets
@ -36,6 +36,7 @@ class ImageCacheManager {
return currentMajor > cachedMajor
}
// 返回对应的缓存
async getCacheForUrl(url) {
// 规范化 URL
const normalizedUrl = this.normalizeUrl(url)
@ -113,24 +114,22 @@ class ImageCacheManager {
// 检查是否是major版本更新
const isMajorUpdate = this.isMajorVersionUpdate(cachedVersion, this.currentAppVersion)
// major版本更新清除所有缓存
if (isMajorUpdate) {
// major版本更新清除所有缓存
console.log(
`检测到major版本更新${cachedVersion} 更新到 ${this.currentAppVersion},清除所有缓存`
)
console.log(`major版本更新清除所有缓存`)
await this.clearAllCaches()
} else {
// 非major版本更新检查图片版本是否变化
}
// 非major版本更新检查图片版本是否变化
else {
const isImageVersionChanged = this.isVersionChanged(
cachedImageVersion || '1.0.0',
IMAGE_CACHE_VERSION.version
)
// 图片版本变化:根据配置清除指定类型的缓存
if (isImageVersionChanged) {
// 图片版本变化:根据配置清除指定类型的缓存
console.log(
`检测到图片版本变化,从 ${cachedImageVersion} 更新到 ${IMAGE_CACHE_VERSION.version},根据配置清除缓存类型: ${IMAGE_CACHE_VERSION.cacheType}`
)
console.log(`非major版本更新清除缓存类型: ${IMAGE_CACHE_VERSION.cacheType}`)
await this.clearCacheByType(IMAGE_CACHE_VERSION.cacheType)
}
// 如果图片版本未变化且不是major更新则不执行任何清除操作
@ -154,8 +153,8 @@ class ImageCacheManager {
await this.clearAllCaches()
break
default:
// 默认清除图片缓存
await this.clearOldImageCaches()
// 默认清除全部图片缓存
await this.clearAllCaches()
break
}
}
@ -165,12 +164,13 @@ class ImageCacheManager {
try {
const keys = await cache.keys()
// 如果同时满足数量和大小限制,则无需处理
// 检查数量限制
if (keys.length <= this.maxCacheItems) {
// 快速估算大小检查基于headers
let estimatedTotalSize = 0
let needDetailedCheck = false
// 计算缓存大小
for (const key of keys) {
const response = await cache.match(key)
if (response) {
@ -185,12 +185,14 @@ class ImageCacheManager {
}
}
// 如果估计大小在限制内且不需要详细检查,则直接返回
// 检查大小限制
// 无需检查,亦或是缓存数量和大小都符合要求,直接返回
if (!needDetailedCheck && estimatedTotalSize <= this.maxCacheSize) {
return
}
}
// 缓存项超出限制,需要清理
// 获取所有缓存项及其大小信息
const cacheEntries = []
for (const key of keys) {
@ -210,17 +212,14 @@ class ImageCacheManager {
}
// 计算当前总数和总大小
let totalItems = cacheEntries.length
let totalSize = cacheEntries.reduce((sum, entry) => sum + entry.size, 0)
let totalItems = cacheEntries.length //当前总数
let totalSize = cacheEntries.reduce((sum, entry) => sum + entry.size, 0) //总大小
// 确定需要删除的项数(基于数量限制)
let itemsToRemoveCount = Math.max(0, totalItems - this.maxCacheItems)
// 确定需要删除的额外项数(基于大小限制)
let sizeToRemove = Math.max(0, totalSize - this.maxCacheSize)
let itemsToRemoveCount = Math.max(0, totalItems - this.maxCacheItems) // 需要删除的项数
let sizeToRemove = Math.max(0, totalSize - this.maxCacheSize) // 需要删除的项数
let itemsToDelete = [] //需要删除的项
// 综合两个限制条件,确定实际需要删除的项
let itemsToDelete = []
if (itemsToRemoveCount > 0 || sizeToRemove > 0) {
// 按照最旧的顺序排序keys已经是按顺序的
// 逐个删除直到满足两个条件
@ -248,7 +247,7 @@ class ImageCacheManager {
}
}
// 清除全部旧图片缓存(仅在图片版本更新时调用)
// 清除的OSS图片缓存
async clearOldImageCaches() {
if (!('caches' in window)) return
@ -259,14 +258,14 @@ class ImageCacheManager {
name.startsWith('likei-images-v') && !name.includes(`v${this.currentImageVersion}`) // 排除当前版本的缓存
)
console.log('正在清除旧图片缓存:', oldImageCaches)
console.log('正在清除旧的OSS图片缓存:', oldImageCaches)
await Promise.all(oldImageCaches.map((name) => caches.delete(name)))
} catch (error) {
console.warn('清理旧图片缓存失败:', error)
console.warn('清理旧的OSS图片缓存失败:', error)
}
}
// 清除全部旧资产缓存(仅在图片版本更新时调用)
// 清除旧的本地assets缓存
async clearOldAssetCaches() {
if (!('caches' in window)) return
@ -277,14 +276,14 @@ class ImageCacheManager {
name.startsWith('likei-assets-v') && !name.includes(`v${this.currentImageVersion}`) // 排除当前版本的缓存
)
console.log('正在清除旧资产缓存:', oldAssetCaches)
console.log('正在清除旧的本地assets缓存:', oldAssetCaches)
await Promise.all(oldAssetCaches.map((name) => caches.delete(name)))
} catch (error) {
console.warn('清理旧资产缓存失败:', error)
console.warn('清理旧的本地assets缓存失败:', error)
}
}
// 清除所有缓存仅在major版本更新时调用
// 清除所有图片缓存OSS图片、本地assets图片
async clearAllCaches() {
if (!('caches' in window)) return
@ -294,10 +293,10 @@ class ImageCacheManager {
(name) => name.startsWith('likei-images-v') || name.startsWith('likei-assets-v')
)
console.log('正在清除所有缓存:', allCaches)
console.log('正在清除所有图片缓存:', allCaches)
await Promise.all(allCaches.map((name) => caches.delete(name)))
} catch (error) {
console.warn('清理所有缓存失败:', error)
console.warn('清理所有图片缓存失败:', error)
}
}
@ -368,7 +367,7 @@ class ImageCacheManager {
}
}
// 获取缓存的图片 Blob URL
// 获取缓存的图片 Blob URL(如果是缓存的图片会返回 blob URL否则返回原始 URL
async getCacheImageUrl(url) {
if (!('caches' in window)) return url
@ -376,6 +375,7 @@ class ImageCacheManager {
// 规范化 URL
const normalizedUrl = this.normalizeUrl(url)
// 目标符合缓存条件
if (this.isBuildAsset(normalizedUrl)) {
const cache = await this.getCacheForUrl(normalizedUrl)
@ -387,38 +387,40 @@ class ImageCacheManager {
})
let cachedResponse = await cache.match(request)
// 2. 如果精确匹配失败,尝试直接使用 URL 字符串匹配
if (!cachedResponse) {
// 2. 如果精确匹配失败,尝试直接使用 URL 字符串匹配
cachedResponse = await cache.match(normalizedUrl)
}
// 3. 如果还是没找到,尝试使用 URL 的 origin + pathname去掉查询参数
if (!cachedResponse) {
// 3. 如果还是没找到,尝试使用 URL 的 origin + pathname去掉查询参数
const urlObj = new URL(normalizedUrl)
const urlWithoutQuery = `${urlObj.origin}${urlObj.pathname}`
cachedResponse = await cache.match(urlWithoutQuery)
}
// 4. 尝试匹配去掉协议的 URL
if (!cachedResponse) {
// 4. 尝试匹配去掉协议的 URL
const urlWithoutProtocol = normalizedUrl.replace(/^https?:\/\//, '')
cachedResponse = await cache.match(urlWithoutProtocol)
}
// 经过上面流程后,匹配成功
if (cachedResponse) {
const blob = await cachedResponse.blob()
return URL.createObjectURL(blob)
} else {
// 缓存未命中,触发预缓存
const blob = await cachedResponse.blob() // 获取命中缓存对象的blob
return URL.createObjectURL(blob) // 返回创建的 Blob URL
}
// 经过上面流程后,缓存依旧未命中
else {
try {
await this.cacheImage(normalizedUrl)
await this.cacheImage(normalizedUrl) // 触发预缓存
} catch (cacheError) {
console.warn(`预缓存失败: ${cacheError}`)
}
}
}
// 如果没有缓存,返回原始 URL
return url
return url // 不符合缓存条件,直接返回原始 URL
} catch (error) {
console.warn('获取缓存图片 URL 失败:', error)
return url
@ -435,7 +437,7 @@ class ImageCacheManager {
// 只从缓存获取本地资源
if (this.isBuildAsset(normalizedUrl)) {
const cache = await this.getCacheForUrl(normalizedUrl) // 使用相同的逻辑选择缓存
const cache = await this.getCacheForUrl(normalizedUrl) // 选中对应缓存
// 尝试多种匹配方式
let cachedResponse = await cache.match(
@ -471,44 +473,39 @@ class ImageCacheManager {
// 1. 请求去重:检查是否有相同请求正在进行
if (this.pendingRequests.has(normalizedSrc)) {
// 返回现有的Promise避免重复请求
return this.pendingRequests.get(normalizedSrc)
return this.pendingRequests.get(normalizedSrc) // 返回对应的Promise避免重复请求
}
// 2. 创建新的加载Promise
// 2. 请求列表中不存在,则创建新的加载Promise
const loadPromise = new Promise(async (resolve, reject) => {
try {
// 3. 尝试从缓存获取
const cachedResponse = await this.getCachedImage(normalizedSrc)
const cachedResponse = await this.getCachedImage(normalizedSrc) // 3. 获取对应的缓存图片
// 4. 缓存命中,处理图片
if (cachedResponse) {
// 4. 缓存命中,处理图片
const blob = await cachedResponse.blob()
const imageUrl = URL.createObjectURL(blob)
// 5. 创建Image对象并设置src
const img = new Image()
const img = new Image() // 5. 创建Image对象并设置src
// 6. 加载成功清理临时URL并resolve
img.onload = () => {
// 6. 加载成功清理临时URL并resolve
URL.revokeObjectURL(imageUrl)
URL.revokeObjectURL(imageUrl) // 清理临时URL
resolve(img)
}
// 7. 关键:加载失败时的处理
img.onerror = () => {
// 8. 清理临时URL
URL.revokeObjectURL(imageUrl)
// 9. 回退到网络加载
this.loadImageFromNetwork(normalizedSrc, resolve, reject)
URL.revokeObjectURL(imageUrl) // 8. 清理临时URL
this.loadImageFromNetwork(normalizedSrc, resolve, reject) // 9. 回退到网络加载
}
img.src = imageUrl // 10. 开始加载
return
}
// 11. 缓存中没有或不是本地资源,从网络加载
this.loadImageFromNetwork(normalizedSrc, resolve, reject)
this.loadImageFromNetwork(normalizedSrc, resolve, reject) // 11. 缓存中没有或不是本地资源,从网络加载
} catch (error) {
this.loadImageFromNetwork(normalizedSrc, resolve, reject)
this.loadImageFromNetwork(normalizedSrc, resolve, reject) // 12. 网络加载失败,回退到网络加载
}
})
@ -516,11 +513,10 @@ class ImageCacheManager {
this.pendingRequests.set(normalizedSrc, loadPromise)
try {
const result = await loadPromise
const result = await loadPromise // 等待加载完成
return result
} finally {
// 13. 清理记录
this.pendingRequests.delete(normalizedSrc)
this.pendingRequests.delete(normalizedSrc) // 13. 清理记录
}
}
@ -537,23 +533,24 @@ class ImageCacheManager {
cache: 'no-store', // 强制不使用 HTTP 缓存
})
// 加载完成
if (response.ok) {
// 克隆响应,一份用于缓存,一份用于图片加载
const isAsset = this.isBuildAsset(normalizedSrc)
const responseForCache = isAsset ? response.clone() : null
const isAsset = this.isBuildAsset(normalizedSrc) // 检查是否为适合缓存的资源
const responseForCache = isAsset ? response.clone() : null //复制适合缓存的资源
const responseForImage = response // 原始响应用于图片加载
// 先缓存响应
// 适合缓存
if (isAsset) {
try {
// 使用 getCacheForUrl 确保使用正确的缓存
const cache = await this.getCacheForUrl(normalizedSrc)
await cache.put(normalizedSrc, responseForCache) // 使用克隆的响应进行缓存
const cache = await this.getCacheForUrl(normalizedSrc) // 获取对应的缓存
await cache.put(normalizedSrc, responseForCache) // 缓存克隆的响应
} catch (cacheError) {
console.warn('缓存图片时出错:', cacheError)
}
}
// 创建Image对象并设置src
const img = new Image()
img.onload = () => {
resolve(img)
@ -562,7 +559,9 @@ class ImageCacheManager {
reject(error)
}
img.src = URL.createObjectURL(await responseForImage.blob()) // 使用原始响应
} else {
}
// 加载失败
else {
reject(new Error(`HTTP ${response.status}`))
}
} catch (error) {
@ -570,7 +569,7 @@ class ImageCacheManager {
}
}
// 添加辅助方法来获取缓存名称
// 获取当前版本的缓存名称
getCacheNameByUrl(url) {
const normalizedUrl = this.normalizeUrl(url)
@ -585,43 +584,6 @@ class ImageCacheManager {
return this.imageCacheName
}
}
// 为OSS图片创建缓存的特殊处理
async cacheImageFromImage(src, img) {
try {
// 检查浏览器是否支持缓存API以及资源是否适合缓存
if (!('caches' in window) || !this.isBuildAsset(src)) return
const cache = await this.getCacheForUrl(src)
const cachedResponse = await cache.match(src)
// 只有当图片尚未缓存时才进行缓存操作
if (!cachedResponse) {
// 创建canvas元素来处理图片
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0) // 将图片绘制到canvas上
// 将canvas转换为blob对象
canvas.toBlob(async (blob) => {
if (blob) {
// 创建一个新的Response对象来模拟网络响应
const response = new Response(blob, {
headers: {
'Content-Type': 'image/png',
},
})
// 将图片存入缓存
await cache.put(src, response.clone())
}
})
}
} catch (error) {
console.warn('缓存OSS图片失败:', src, error)
}
}
}
// 导出单例

View File

@ -13,37 +13,48 @@ const PERSISTENT_KEYS = [
export class VersionChecker {
constructor() {
this.currentVersion = import.meta.env.VITE_APP_VERSION || Date.now().toString()
this.storageKey = 'h5_app_version'
this.serverVersionKey = 'app_server_version'
this.enableCheck = import.meta.env.VITE_ENABLE_VERSION_CHECK === 'true'
this.currentVersion = import.meta.env.VITE_APP_VERSION || Date.now().toString() //app版本号
this.storageKey = 'h5_app_version' //本地存储的版本号key
this.serverVersionKey = 'app_server_version' //服务器版本号key
this.enableCheck = import.meta.env.VITE_ENABLE_VERSION_CHECK === 'true' //是否开启检测app版本号
}
// 检查服务器版本
// 检查版本(开发环境:本地.env文件生产环境构建脚本生成的version.json文件
async checkServerVersion() {
if (!this.enableCheck) {
return false
}
try {
// 强制从服务器获取最新版本信息,避免缓存
const response = await fetch(`/version.json?t=${Date.now()}`, {
cache: 'no-cache',
headers: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
},
})
let versionInfo //版本更新信息
if (!response.ok) {
console.warn('无法获取服务器版本信息')
return false
// 开发环境:使用本地.env文件环境变量
if (import.meta.env.MODE === 'development') {
versionInfo = {
version: import.meta.env.VITE_APP_VERSION,
type: import.meta.env.VITE_VERSION_TYPE || 'patch',
}
}
// 生产环境从服务器获取version.json文件
else {
const response = await fetch(`/version.json?t=${Date.now()}`)
if (!response.ok) {
console.warn('无法获取服务器版本信息')
return false
}
const contentType = response.headers.get('content-type')
if (!contentType || !contentType.includes('application/json')) {
console.warn('服务器返回的不是JSON格式')
return false
}
versionInfo = await response.json() //获取服务器版本信息
}
const versionInfo = await response.json()
const serverVersion = versionInfo.version
const versionType = versionInfo.type || 'patch' // 获取版本类型
const storedServerVersion = localStorage.getItem(this.serverVersionKey)
const serverVersion = versionInfo.version //最新生成的服务器版本号
const versionType = versionInfo.type || 'patch' // 版本更新类型
const storedServerVersion = localStorage.getItem(this.serverVersionKey) //网页缓存的版本号
// 更新服务器版本信息
localStorage.setItem(this.serverVersionKey, serverVersion)
@ -61,16 +72,17 @@ export class VersionChecker {
return { shouldUpdate: false }
} catch (error) {
console.warn('版本检查失败:', error)
console.warn('版本检查失败:', error.message)
return { shouldUpdate: false }
}
}
// 检查版本更新(综合方法)
// 初始化网页首次检查版本
async checkVersion() {
// 首先检查服务器版本
// 获取最新版本信息
const versionResult = await this.checkServerVersion()
// 判断是否需要更新
if (versionResult.shouldUpdate) {
await this.handleVersionUpdate(versionResult.versionType)
return true
@ -118,10 +130,10 @@ export class VersionChecker {
this.forceReload()
}
// 添加专门处理 session 数据的方法
// 清除所有会话存储数据sessionStorage
clearSessionData() {
try {
// 保存需要保留的数据
// localStorage和Cache API不受影响
const preservedData = {}
PERSISTENT_KEYS.forEach((key) => {
const value = localStorage.getItem(key)
@ -142,8 +154,14 @@ export class VersionChecker {
}
}
// 建议改为 async/await 风格以保持一致性
async clearCache() {
// 清除范围:
// localStorage所有本地存储数据
// sessionStorage所有会话存储数据
// Cache API所有缓存包括非图片缓存
// Service Worker注销所有注册
// 保留数据:保留 PERSISTENT_KEYS 中定义的白名单数据
try {
// 保存需要保留的数据
const preservedData = {}