如何避免页面刷新时重复调用v-img组件加载同一图片?
实现图片仅加载一次(含刷新页面场景)
要让图片仅加载一次,即使刷新页面也不重复请求,可从浏览器缓存、前端手动缓存、状态管理缓存三个方向实现,以下是具体方案:
1. 利用浏览器HTTP缓存(推荐优先使用)
浏览器默认会对静态资源(包括图片)做缓存,只要服务器给图片资源配置合适的响应头,刷新页面时浏览器会直接读取本地缓存,不会发起重复请求。
关键响应头配置
服务器需返回以下类型的响应头(以Nginx为例):
location ~* \.(jpeg|jpg|png)$ { expires 1y; # 缓存1年 add_header Cache-Control "public, max-age=31536000"; add_header ETag $file_modified; # 资源变更验证标识 }
配置后,浏览器首次加载图片会缓存到本地,后续刷新或重新打开页面时,只要缓存未过期就直接用本地副本;过期后会通过ETag验证资源是否更新,仅在资源变化时才重新下载。
2. 前端手动缓存(适用于需精细控制的场景)
如果无法控制服务器配置,或需要强制本地缓存,可将图片转成Blob存储到localStorage,下次加载直接读取本地缓存生成URL。
组件内实现示例
<template> <v-img height="450" :src="cachedImageSrc" :lazy-src="cachedLazyImageSrc" class="d-none d-md-block" alt="001" ></v-img> </template> <script> export default { data() { return { originalSrc: 'https://images.pexels.com/photos/10480463/pexels-photo-10480463.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', originalLazySrc: 'https://images.pexels.com/photos/10877192/pexels-photo-10877192.jpeg', cachedImageSrc: '', cachedLazyImageSrc: '' } }, async mounted() { this.cachedImageSrc = await this.cacheImage(this.originalSrc) this.cachedLazyImageSrc = await this.cacheImage(this.originalLazySrc) }, methods: { async cacheImage(url) { const cacheKey = `image_${btoa(url)}` const cachedBase64 = localStorage.getItem(cacheKey) // 存在缓存则直接生成Blob URL if (cachedBase64) { const blob = new Blob([Uint8Array.from(atob(cachedBase64), c => c.charCodeAt(0))]) return URL.createObjectURL(blob) } // 无缓存则请求图片并存储 const response = await fetch(url) const blob = await response.blob() return new Promise(resolve => { const reader = new FileReader() reader.onloadend = () => { const base64Str = reader.result.split(',')[1] localStorage.setItem(cacheKey, base64Str) resolve(URL.createObjectURL(blob)) } reader.readAsDataURL(blob) }) } }, beforeUnmount() { // 释放Blob URL,避免内存泄漏 URL.revokeObjectURL(this.cachedImageSrc) URL.revokeObjectURL(this.cachedLazyImageSrc) } } </script>
注意事项
localStorage有存储上限(通常为5MB),不适合缓存大尺寸图片;- 页面卸载时需调用
URL.revokeObjectURL释放Blob资源,避免内存泄漏。
3. 结合状态管理缓存(多组件共享场景)
如果图片资源需要在多个组件间共享,可将缓存逻辑放到Vuex/Pinia中,配合asyncData(Nuxt场景)实现页面刷新后仍能复用缓存。
Pinia缓存模块示例
// stores/imageCache.js import { defineStore } from 'pinia' export const useImageCacheStore = defineStore('imageCache', { state: () => ({ images: {} }), actions: { async fetchAndCache(url) { const cacheKey = `image_${btoa(url)}` // 优先读取内存缓存 if (this.images[cacheKey]) return this.images[cacheKey] // 内存无缓存则读取localStorage持久化缓存 const cachedBase64 = localStorage.getItem(cacheKey) if (cachedBase64) { const blob = new Blob([Uint8Array.from(atob(cachedBase64), c => c.charCodeAt(0))]) const blobUrl = URL.createObjectURL(blob) this.images[cacheKey] = blobUrl return blobUrl } // 无缓存则请求资源并存储 const response = await fetch(url) const blob = await response.blob() const blobUrl = URL.createObjectURL(blob) // 转base64存入localStorage持久化 const reader = new FileReader() reader.onloadend = () => { const base64Str = reader.result.split(',')[1] localStorage.setItem(cacheKey, base64Str) } reader.readAsDataURL(blob) this.images[cacheKey] = blobUrl return blobUrl } } })
组件中使用(Nuxt asyncData场景)
<template> <v-img height="450" :src="cachedImageSrc" :lazy-src="cachedLazyImageSrc" class="d-none d-md-block" alt="001" ></v-img> </template> <script setup> import { useImageCacheStore } from '~/stores/imageCache' const imageCacheStore = useImageCacheStore() const originalSrc = 'https://images.pexels.com/photos/10480463/pexels-photo-10480463.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1' const originalLazySrc = 'https://images.pexels.com/photos/10877192/pexels-photo-10877192.jpeg' const cachedImageSrc = await imageCacheStore.fetchAndCache(originalSrc) const cachedLazyImageSrc = await imageCacheStore.fetchAndCache(originalLazySrc) onUnmounted(() => { URL.revokeObjectURL(cachedImageSrc) URL.revokeObjectURL(cachedLazyImageSrc) }) </script>
内容的提问来源于stack exchange,提问作者MonLes
相关产品推荐
相关产品推荐

