/** * 图片预加载工具类 - 针对微信小程序优化 */ class ImagePreloader { constructor() { this.cache = new Map() // 图片缓存 this.loadingQueue = new Map() // 正在加载的队列 this.maxConcurrent = 3 // 最大并发加载数 this.currentLoading = 0 // 当前正在加载的数量 } /** * 预加载图片 * @param {string} src 图片地址 * @param {Object} options 选项 * @returns {Promise} */ preload(src, options = {}) { if (!src) return Promise.reject(new Error('Image src is required')) // 如果已经缓存,直接返回 if (this.cache.has(src)) { return Promise.resolve(src) } // 如果正在加载,返回现有Promise if (this.loadingQueue.has(src)) { return this.loadingQueue.get(src) } // 创建加载Promise const loadPromise = this._loadImage(src, options) this.loadingQueue.set(src, loadPromise) loadPromise.finally(() => { this.loadingQueue.delete(src) this.currentLoading-- this._processQueue() // 处理等待队列 }) return loadPromise } /** * 批量预加载图片 * @param {Array} srcList 图片地址数组 * @param {Object} options 选项 * @returns {Promise} */ preloadBatch(srcList, options = {}) { if (!Array.isArray(srcList)) return Promise.reject(new Error('srcList must be an array')) return Promise.allSettled( srcList.map(src => this.preload(src, options)) ) } /** * 实际加载图片的方法 * @private */ _loadImage(src, options = {}) { return new Promise((resolve, reject) => { // 控制并发数量 if (this.currentLoading >= this.maxConcurrent) { setTimeout(() => { this._loadImage(src, options).then(resolve).catch(reject) }, 100) return } this.currentLoading++ // 优化图片URL const optimizedSrc = this._optimizeImageUrl(src, options) // 使用微信小程序的图片预加载API uni.preloadPage({ url: optimizedSrc, success: () => { this.cache.set(src, optimizedSrc) resolve(optimizedSrc) }, fail: (error) => { // 如果预加载失败,尝试直接加载 this._fallbackLoad(optimizedSrc).then(() => { this.cache.set(src, optimizedSrc) resolve(optimizedSrc) }).catch(reject) } }) // 设置超时 setTimeout(() => { if (!this.cache.has(src)) { reject(new Error('Image load timeout')) } }, options.timeout || 8000) }) } /** * 备用加载方法 * @private */ _fallbackLoad(src) { return new Promise((resolve, reject) => { const img = uni.createImage ? uni.createImage() : new Image() img.onload = () => resolve(src) img.onerror = reject img.src = src }) } /** * 优化图片URL * @private */ _optimizeImageUrl(src, options = {}) { if (!src || typeof src !== 'string') return src // 如果是本地图片或base64,直接返回 if (src.startsWith('/') || src.startsWith('data:')) return src // 如果包含已知的压缩参数,直接返回 if (src.includes('x-oss-process') || src.includes('imageView2')) return src const { width = 300, height = 300, quality = 80 } = options // 针对不同CDN服务商添加压缩参数 if (src.includes('eshangtech.com')) { // 阿里云OSS return `${src}${src.includes('?') ? '&' : '?'}x-oss-process=image/resize,w_${width},h_${height},m_fill/quality,q_${quality}` } else if (src.includes('qcloud.com') || src.includes('myqcloud.com')) { // 腾讯云COS return `${src}${src.includes('?') ? '&' : '?'}imageView2/1/w/${width}/h/${height}/q/${quality}` } else if (src.includes('baidubce.com')) { // 百度云BOS return `${src}${src.includes('?') ? '&' : '?'}x-bce-process=image/resize,w_${width},h_${height}/quality,q_${quality}` } return src } /** * 处理等待队列 * @private */ _processQueue() { // 这里可以添加队列处理逻辑,目前保持简单 } /** * 清除缓存 */ clearCache() { this.cache.clear() } /** * 获取缓存状态 */ getCacheInfo() { return { cacheSize: this.cache.size, loadingCount: this.loadingQueue.size, currentLoading: this.currentLoading } } } // 创建单例 const imagePreloader = new ImagePreloader() export default imagePreloader