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();
}
});