feat(图片缓存): 优化方法
This commit is contained in:
parent
f4cea451ba
commit
93c679fa3c
@ -4,8 +4,8 @@ class ImageCacheManager {
|
|||||||
this.cacheVersionKey = 'likei-image-cache-version'
|
this.cacheVersionKey = 'likei-image-cache-version'
|
||||||
this.currentAppVersion = import.meta.env.VITE_APP_VERSION || '1.0.0'
|
this.currentAppVersion = import.meta.env.VITE_APP_VERSION || '1.0.0'
|
||||||
this.cacheName = `likei-images-v${this.currentAppVersion}`
|
this.cacheName = `likei-images-v${this.currentAppVersion}`
|
||||||
this.maxCacheItems = 100 // 限制最多缓存50张图片(可根据需要调整)
|
this.maxCacheItems = 100 // 最大缓存数量(100张)
|
||||||
this.maxCacheSize = 100 * 1024 * 1024 // 限制总大小50MB(可根据需要调整)
|
this.maxCacheSize = 100 * 1024 * 1024 // 最大缓存大小(100MB)
|
||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,23 +35,91 @@ class ImageCacheManager {
|
|||||||
try {
|
try {
|
||||||
const keys = await cache.keys()
|
const keys = await cache.keys()
|
||||||
|
|
||||||
// 检查数量限制
|
// 如果同时满足数量和大小限制,则无需处理
|
||||||
if (keys.length > this.maxCacheItems) {
|
if (keys.length <= this.maxCacheItems) {
|
||||||
// 删除最旧的缓存项
|
// 快速估算大小检查(基于headers)
|
||||||
const itemsToRemove = keys.length - this.maxCacheItems
|
let estimatedTotalSize = 0
|
||||||
for (let i = 0; i < itemsToRemove; i++) {
|
let needDetailedCheck = false
|
||||||
await cache.delete(keys[i])
|
|
||||||
console.log(`已删除超出限制的缓存项: ${keys[i].url}`)
|
for (const key of keys) {
|
||||||
|
const response = await cache.match(key)
|
||||||
|
if (response) {
|
||||||
|
const contentLength = response.headers.get('content-length')
|
||||||
|
if (contentLength) {
|
||||||
|
estimatedTotalSize += parseInt(contentLength, 10)
|
||||||
|
} else {
|
||||||
|
// 如果有任何一项没有content-length头,需要详细检查
|
||||||
|
needDetailedCheck = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果估计大小在限制内且不需要详细检查,则直接返回
|
||||||
|
if (!needDetailedCheck && estimatedTotalSize <= this.maxCacheSize) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 可选:检查大小限制(需要估算)
|
// 获取所有缓存项及其大小信息
|
||||||
// 这里简化处理,实际项目中可以更精确计算
|
const cacheEntries = []
|
||||||
|
for (const key of keys) {
|
||||||
|
try {
|
||||||
|
const response = await cache.match(key)
|
||||||
|
if (response) {
|
||||||
|
const blob = await response.blob()
|
||||||
|
cacheEntries.push({
|
||||||
|
key,
|
||||||
|
size: blob.size,
|
||||||
|
response,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('无法获取缓存项大小:', key.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算当前总数和总大小
|
||||||
|
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 itemsToDelete = []
|
||||||
|
if (itemsToRemoveCount > 0 || sizeToRemove > 0) {
|
||||||
|
// 按照最旧的顺序排序(keys已经是按顺序的)
|
||||||
|
// 逐个删除直到满足两个条件
|
||||||
|
let removedSize = 0
|
||||||
|
let removedCount = 0
|
||||||
|
|
||||||
|
for (const entry of cacheEntries) {
|
||||||
|
// 如果已经满足两个条件,则停止删除
|
||||||
|
if (removedCount >= itemsToRemoveCount && totalSize - removedSize <= this.maxCacheSize) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsToDelete.push(entry.key)
|
||||||
|
removedSize += entry.size
|
||||||
|
removedCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行删除操作
|
||||||
|
for (const key of itemsToDelete) {
|
||||||
|
await cache.delete(key)
|
||||||
|
console.log(`已删除超出限制的缓存项: ${key.url}`)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('检查缓存限制时出错:', error)
|
console.warn('检查缓存限制时出错:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 清除全部旧图片缓存
|
||||||
async clearOldCaches() {
|
async clearOldCaches() {
|
||||||
if (!('caches' in window)) return
|
if (!('caches' in window)) return
|
||||||
|
|
||||||
@ -68,7 +136,7 @@ class ImageCacheManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更严格的判断是否为本地构建资源
|
//判断是否为适合缓存资源
|
||||||
isBuildAsset(url) {
|
isBuildAsset(url) {
|
||||||
try {
|
try {
|
||||||
// 如果是相对路径,肯定是本地资源
|
// 如果是相对路径,肯定是本地资源
|
||||||
@ -116,9 +184,6 @@ class ImageCacheManager {
|
|||||||
const cachedResponse = await cache.match(url)
|
const cachedResponse = await cache.match(url)
|
||||||
|
|
||||||
if (!cachedResponse) {
|
if (!cachedResponse) {
|
||||||
// 先检查并清理超出限制的缓存
|
|
||||||
// await this.enforceCacheLimits(cache)
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
credentials: 'omit',
|
credentials: 'omit',
|
||||||
@ -126,6 +191,9 @@ class ImageCacheManager {
|
|||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
await cache.put(url, response.clone())
|
await cache.put(url, response.clone())
|
||||||
console.log(`图片已缓存: ${url}`)
|
console.log(`图片已缓存: ${url}`)
|
||||||
|
|
||||||
|
// 添加新图片后立即检查并强制执行限制
|
||||||
|
await this.enforceCacheLimits(cache)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,6 +249,7 @@ class ImageCacheManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从网络加载图片
|
||||||
async loadImageFromNetwork(src, resolve, reject) {
|
async loadImageFromNetwork(src, resolve, reject) {
|
||||||
try {
|
try {
|
||||||
// 对于OSS资源,先尝试直接创建Image对象(绕过fetch的CORS问题)
|
// 对于OSS资源,先尝试直接创建Image对象(绕过fetch的CORS问题)
|
||||||
@ -235,26 +304,31 @@ class ImageCacheManager {
|
|||||||
// 为OSS图片创建缓存的特殊处理
|
// 为OSS图片创建缓存的特殊处理
|
||||||
async cacheImageFromImage(src, img) {
|
async cacheImageFromImage(src, img) {
|
||||||
try {
|
try {
|
||||||
|
// 检查浏览器是否支持缓存API以及资源是否适合缓存
|
||||||
if (!('caches' in window) || !this.isBuildAsset(src)) return
|
if (!('caches' in window) || !this.isBuildAsset(src)) return
|
||||||
|
|
||||||
const cache = await caches.open(this.cacheName)
|
const cache = await caches.open(this.cacheName)
|
||||||
const cachedResponse = await cache.match(src)
|
const cachedResponse = await cache.match(src)
|
||||||
|
|
||||||
|
// 只有当图片尚未缓存时才进行缓存操作
|
||||||
if (!cachedResponse) {
|
if (!cachedResponse) {
|
||||||
// 创建一个模拟的响应来缓存
|
// 创建canvas元素来处理图片
|
||||||
const canvas = document.createElement('canvas')
|
const canvas = document.createElement('canvas')
|
||||||
canvas.width = img.width
|
canvas.width = img.width
|
||||||
canvas.height = img.height
|
canvas.height = img.height
|
||||||
const ctx = canvas.getContext('2d')
|
const ctx = canvas.getContext('2d')
|
||||||
ctx.drawImage(img, 0, 0)
|
ctx.drawImage(img, 0, 0) // 将图片绘制到canvas上
|
||||||
|
|
||||||
|
// 将canvas转换为blob对象
|
||||||
canvas.toBlob(async (blob) => {
|
canvas.toBlob(async (blob) => {
|
||||||
if (blob) {
|
if (blob) {
|
||||||
|
// 创建一个新的Response对象来模拟网络响应
|
||||||
const response = new Response(blob, {
|
const response = new Response(blob, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'image/png',
|
'Content-Type': 'image/png',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
// 将图片存入缓存
|
||||||
await cache.put(src, response.clone())
|
await cache.put(src, response.clone())
|
||||||
console.log(`OSS图片已缓存: ${src}`)
|
console.log(`OSS图片已缓存: ${src}`)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ export const preloadImages = (sources, options = {}) => {
|
|||||||
const { useSmartCache = false } = options
|
const { useSmartCache = false } = options
|
||||||
let urls = []
|
let urls = []
|
||||||
|
|
||||||
|
// 支持数组和对象形式的图片源
|
||||||
if (Array.isArray(sources)) {
|
if (Array.isArray(sources)) {
|
||||||
urls = sources
|
urls = sources
|
||||||
} else if (typeof sources === 'object') {
|
} else if (typeof sources === 'object') {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user