Reputation: 2178
I've set up Workbox using InjectManifest (just setting properties swSrc
and swDest
), and created my service worker (attached below).
Everything works great when I start from the root of the site, but if I start from another page, one that is otherwise handled by React Router (for example, by reloading the page), the service worker gives me the error: Cannot get /page
, where "page" is the URL route that I'm trying to load.
As far as I can tell, I don't have anything in my service worker that would indicate how these routes should be handled, but it seems that the service worker is preventing the index.html
file from being loaded when a random URL string is being requested.
I have tried to use registerRoute(new NavigationRoute(createHandlerBoundToURL('/index.html')))
, but then I get an error that index.html
isn't pre-cached. I don't want to cache index.html
, as I don't want to end up in the situation where a new service worker will never be downloaded because an old cached page is always being served up..
What else can be done?
import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin';
import { CacheFirst } from 'workbox-strategies/CacheFirst';
import { StaleWhileRevalidate } from 'workbox-strategies/StaleWhileRevalidate';
import { ExpirationPlugin } from 'workbox-expiration/ExpirationPlugin';
import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute';
import { registerRoute } from 'workbox-routing/registerRoute';
import { setCatchHandler } from 'workbox-routing/setCatchHandler';
import { setDefaultHandler } from 'workbox-routing/setDefaultHandler';
import { clientsClaim } from 'workbox-core';
import { NetworkOnly } from 'workbox-strategies';
const imageFallback = `/media/catalog/product/p/l/placeholder.jpg`;
precacheAndRoute(self.__WB_MANIFEST);
clientsClaim();
setDefaultHandler(new NetworkOnly());
registerRoute(
new RegExp('(robots.txt|favicon.ico|manifest.json)'),
new StaleWhileRevalidate()
);
// Handle images hosted with the PWA, that means they'll be served from the same origin
registerRoute(
({ url, sameOrigin }) => {
// Only cache images from the same origin
if (!sameOrigin) return false;
return url.pathname.match(/\.(?:png|gif|jpg|jpeg|svg|pjpg|webp)$/);
},
new StaleWhileRevalidate({
cacheName: 'local-images',
matchOptions: {
ignoreVary: true
},
plugins: [
new ExpirationPlugin({
// Keep at most 100 entries
maxEntries: 100,
// Don't keep any entries for more than 7 days
maxAgeSeconds: 7 * 24 * 60 * 60,
// Automatically cleanup if quota is exceeded
purgeOnQuotaError: true
}),
new CacheableResponsePlugin({
statuses: [0, 200]
})
]
})
);
// Handle images loaded from the BE URL. This likely means product images
registerRoute(
({ url }) => {
if (url.origin !== __MAGENTO_BE_URL__) return false;
return url.pathname.match(/\.(?:png|gif|jpg|jpeg|svg|pjpg|webp)$/);
},
new StaleWhileRevalidate({
cacheName: 'remote-images',
matchOptions: {
ignoreVary: true
},
plugins: [
new ExpirationPlugin({
// Keep at most 100 entries
maxEntries: 100,
// Don't keep any entries for more than 7 days
maxAgeSeconds: 7 * 24 * 60 * 60,
// Automatically cleanup if quota is exceeded
purgeOnQuotaError: true
}),
new CacheableResponsePlugin({
statuses: [0, 200]
})
]
})
);
registerRoute(new RegExp(/\.js$/), new CacheFirst());
self.addEventListener('fetch', event => {
const { request } = event;
const responsePromise = router.handleRequest({
event,
request
});
if (responsePromise) {
// Router found a route to handle the request.
event.respondWith(responsePromise);
} else {
// No route was found to handle the request.
// Fallback to network
}
});
const catchHandler = async options => {
const dest = options.request.destination;
const cache = await self.caches.open('workbox-offline-fallbacks');
if (dest === 'image' && imageFallback !== false) {
return (await cache.match(imageFallback)) || Response.error();
}
return Response.error();
};
setCatchHandler(catchHandler);
self.addEventListener('install', event => {
const files = [];
if (imageFallback) {
files.push(imageFallback);
}
event.waitUntil(
self.caches
.open('workbox-offline-fallbacks')
.then(cache => cache.addAll(files))
);
});
self.addEventListener('message', event => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
Upvotes: 1
Views: 1880
Reputation: 2058
Your concerns about nothing is updating anymore are truly valid, there are a lot of pitfalls when working with service worker.
I think your index.html is probably automatically added to the cache with
precacheAndRoute(self.__WB_MANIFEST);
Anyhow, it should be safe to add index.html to the cache, because it is not index.html which decides if there is a new service-worker version, it is the sw.js file itself.
So when /page
is called your service worker can respond with index.html
Most common pitfalls:
You should not rename or relocate the sw.js file in a newer version of your site. Because older, cached versions in the users browser will still look for the old location.
Another thing to keep an eye on is to not mix up cached files with different versions of your site. When publishing and installing a new service worker make sure you clean/update the cache appropriate. Keep in mind that also the HttpCache of the browser could possibly cache "old" versions of your files. A solution to this problem is to add a file hash to your filename stlyes.css => styles-e34f44de.css
Last, but not least, a good idea is to implement a reset button, which unregisters the service worker and cleans the cache. (In case of a mess up, it is much easier to explain your user to click this button, than explain how to opening devtools and reset everything, especially on mobile)
Upvotes: 2