I've been using Google's workbox library for a little while now, on a very basic level. Most of it works great, but for some reason the start_url in my manifest.json isn't being cached on initial page load (reported by Lighthouse, confirmed in dev tools). If you browse around a bit, or soft-refresh the page a few times, it does seem to cache, it just doesn't do it initially.
I assume I'm doing something wrong, but it's not clear to me what that is. Demo URL and source code below.
import { skipWaiting, clientsClaim } from "workbox-core";
import { ExpirationPlugin } from "workbox-expiration";
import { precacheAndRoute } from "workbox-precaching";
import { setCatchHandler, registerRoute } from "workbox-routing";
import { CacheFirst, NetworkFirst } from "workbox-strategies";
skipWaiting();
clientsClaim();
/**
* Cache WordPress content
*/
registerRoute(
({ url }) => {
return (url.pathname && !url.pathname.match(/^\/(wp-admin|wp-content|wp-includes|wp-json|wp-login)/) && url.pathname.match(/\/$/));
},
new NetworkFirst({
cacheName: "new_site-content-cache",
plugins: [
new ExpirationPlugin({
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
})
);
/**
* Cache CSS files
*/
registerRoute(
({ url }) => {
return (url.pathname && url.pathname.match(/\.css$/) && !url.pathname.match(/wp-admin|wp-includes|wp-json/));
},
new CacheFirst({
cacheName: "new_site-css-cache",
plugins: [
new ExpirationPlugin({
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
})
);
/**
* Cache JS files
*/
registerRoute(
({ url }) => {
return (url.pathname && url.pathname.match(/\.js$/) && !url.pathname.match(/wp-admin|wp-includes|wp-json/) && !url.pathname.match(/redirection/));
},
new CacheFirst({
cacheName: "new_site-js-cache",
plugins: [
new ExpirationPlugin({
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
})
);
/**
* Cache image files
*/
registerRoute(
({ url }) => {
return (url.pathname && url.pathname.match(/\.gif|jpeg|jpg|png|svg|webp$/) && !url.pathname.match(/wp-admin|wp-includes|wp-json/));
},
new CacheFirst({
cacheName: "new_site-image-cache",
plugins: [
new ExpirationPlugin({
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
})
);
/**
* Cache font files
*/
registerRoute(
({ url }) => {
return (url.pathname && url.pathname.match(/\.otf|ttf|woff|woff2$/) && !url.pathname.match(/wp-admin|wp-includes|wp-json/));
},
new CacheFirst({
cacheName: "new_site-font-cache",
plugins: [
new ExpirationPlugin({
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
})
);
/**
* Return "offline" page when visiting pages offline that haven't been cached
*/
setCatchHandler(() => {
return caches.match("/offline/");
});
/**
* Precache "offline" page
*/
precacheAndRoute([
{
url: "/offline/",
revision: __VERSION__,
},
]);
UPDATE 1: Okay, so I found this issue which I think is the problem. It seems that the initial response isn't cached because the service worker is installed after the request, so it has nothing to cache. I'm still not seeing a way to fix this, but will post another update if I make any progress.
UPDATE 2: It seems like using skipWaiting() and clientsClaim() is supposed to be the solution to this, but after adding those, it's still not working as expected. It's strange, Lighthouse reports "Current page responds with a 200 when offline," but also "start_url does not respond with a 200 when offlineThe start_url did respond, but not via a service worker." That doesn't make sense because it's the same page... maybe I'm doing something wrong with trying to route /?
UPDATE 3: Tried changing the last check in my first registerRoute to /\/?$/, made no difference. Also tried changing NetworkFirst to CacheFirst and that actually works fine... but I need it to be NetworkFirst, so that's not really a viable option to get around this issue. I also tried precaching /, but that again made no difference.
UPDATE 4: I suspect that this "warm the runtime cache" recipe may be close to what I need, but I haven't been able to successfully implement... if I hard code the start_url, it at least caches the HTML, but misses all other files. It seems like this method of gathering all resources may be useful, but location.href is just returning the service worker URL, and even if it weren't, I'm not sure how I'd split everything out in to their own caches like I'm currently doing.