Service Worker 图片缓存异常:使用Image构造函数加载缓存图片时caches.match返回null
caches.match() Returning Null for Cached Images in Service Workers Hey there, let's walk through why your caches.match(event.request) might be returning null when you try to load cached images via an Image instance, and how to fix this issue step by step.
Common Causes & Fixes
1. Request URL Mismatch (The #1 Culprit)
Service Worker cache matching relies on exact URL matches—even tiny differences will break it. Check these details:
- Protocol: Did you cache
https://your-domain.com/img.jpgbut theImageinstance is requestinghttp://your-domain.com/img.jpg? - Query Parameters: If your image URL has versioning like
img.jpg?v=2, but you cached the URL without the query string, the match will fail. - Trailing Slashes: A URL like
/img/photo.jpgvs/img/photo.jpg/(with trailing slash) are treated as distinct.
Quick Fix:
Print the URL of both the cached request and the Image request to compare:
// In your Service Worker's fetch event console.log('Incoming request URL:', event.request.url); // In your main script when creating the Image const img = new Image(); console.log('Image request URL:', img.src);
If query parameters are the issue, use the ignoreSearch option in caches.match():
caches.match(event.request, { ignoreSearch: true })
2. Request Mode or Headers Mismatch
When you create an Image instance, cross-origin requests default to no-cors mode. If you cached the image using a cors mode request (e.g., via fetch() with mode: 'cors'), the request objects won't match because their mode properties differ.
Fix:
Either:
- Cache images using the same mode as the
Imagerequest (addmode: 'no-cors'when fetching for cache), or - Use the
ignoreVaryoption incaches.match()to bypass header-based mismatches:caches.match(event.request, { ignoreVary: true })
3. The Image Isn't Actually Cached
Double-check if the image exists in your cache storage:
- Open Chrome DevTools → Application tab → Cache Storage.
- Select your cache bucket and verify the target image URL is listed.
If it's missing, your caching logic might be flawed:
- Ensure you're waiting for cache completion in the
installevent withevent.waitUntil(). - Verify that successful network responses are being cached (use
networkResponse.clone()when callingcache.put(), since responses are one-time use).
Example of a reliable install-time cache:
self.addEventListener('install', (event) => { event.waitUntil( caches.open('image-cache-v1') .then((cache) => { return cache.addAll([ '/assets/img/photo1.jpg', '/assets/img/photo2.jpg' ]); }) ); });
4. Service Worker Isn't Active or Intercepting Requests
- Check if your Service Worker is in the Activated state (DevTools → Application → Service Workers). If it's in "Waiting", skip waiting or refresh the page to activate it.
- Ensure your fetch event is explicitly handling image requests. Use
event.request.destination === 'image'to target only image requests:self.addEventListener('fetch', (event) => { if (event.request.destination === 'image') { event.respondWith( caches.match(event.request, { ignoreSearch: true }) .then((cachedResponse) => { // Fall back to network if no cache exists return cachedResponse || fetch(event.request).then((networkResponse) => { caches.open('image-cache-v1').then((cache) => { cache.put(event.request, networkResponse.clone()); }); return networkResponse; }); }) ); } });
5. HTTPS/localhost Requirement
Service Workers only work over HTTPS (or localhost for development). If you're testing on an HTTP domain (not localhost), the Service Worker won't register, and caching won't work at all.
Final Working Example
Here's a complete flow that should resolve the issue:
- Service Worker Code:
// Install: Pre-cache images self.addEventListener('install', (event) => { event.waitUntil( caches.open('image-cache-v1') .then((cache) => cache.addAll(['/img/avatar.jpg', '/img/banner.jpg'])) ); }); // Activate: Clean up old caches (optional but recommended) self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames.filter((name) => name !== 'image-cache-v1') .map((name) => caches.delete(name)) ); }) ); }); // Fetch: Serve cached images, fall back to network self.addEventListener('fetch', (event) => { if (event.request.destination === 'image') { event.respondWith( caches.match(event.request, { ignoreSearch: true }) .then((cached) => cached || fetch(event.request).then((res) => { caches.open('image-cache-v1').then((cache) => cache.put(event.request, res.clone())); return res; })) ); } });
- Main Script Code:
// Register Service Worker first if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js') .then((reg) => console.log('Service Worker registered:', reg)) .catch((err) => console.log('Service Worker registration failed:', err)); }); } // Create Image instance with cached URL const img = new Image(); img.src = '/img/avatar.jpg'; // Exact match to cached URL (or use ignoreSearch if needed) img.onload = () => document.body.appendChild(img);
内容的提问来源于stack exchange,提问作者Dion Zac

