Zima Weather: Service Workers
First, let's get a solid understanding of how resource cache will work without any service workers. For my experiments, I use a translation json `translation.json` that is loaded via i18next.js library.
Local web server
For local testing, I will use a simple http-server
from npm.
Initial Resource load
We clear all caches in Chrome, and perform initial load:
By default, local http-server uses max-age=3600
, so the resource is cached for exactly one hour.
Change Resource
Let's now change a string in the translations.json
:
As expected, changes are not reflected, resource is loaded from disk cache, and it won't be updated for the next hour.
Cloudflare
Now let's repeat same experiments on CloudFlare.
We should be careful though, as we can't use pages.dev domain. It sets max-age=0 hence resources are always requested through the network.
Initial Resource load
Okay, CloudFlare does not cache .json files by default, hence max-age=0, and the resource will be always updated, but for other resources like images, CSS and JS files, it will behave in the same way as the local server.
So let's proceed now to implement a simple Service Worker using the workbox-build
package from Google.
Service Workers
Here is the actual service worker code for Zima Weather:
import {generateSW} from 'workbox-build';
await generateSW({
skipWaiting: true,
globDirectory: 'dist/',
globPatterns: [
'**/*.{png,json,woff2,svg,css,js}'
],
globIgnores: [...],
swDest: 'dist/sw.js',
ignoreURLParametersMatching: [
/^utm_/,
/^fbclid$/,
/^source$/
],
// Adding runtimeCaching configuration
runtimeCaching: [
{
urlPattern: new RegExp('/locales/.*\\.json$'),
handler: 'NetworkFirst',
options: {
cacheName: 'translation-cache',
},
},
{
urlPattern: /\.(?:html|svg|json|png)$/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'assets-cache',
},
},
],
});
There are many parameters here, but the important bit here is the runtimeCaching
configuration. We configure two caches with different strategies:
translation-cache
usesNetworkFirst
strategy, which means it will always try to load the resources over the network first. NB: urlPattern regex is matching against full urls like http://localhost:8080/path, and not just relative path, so don't use ^ in regex unless it's a full url.- Other resources use
StaleWhileRevalidate
strategy – the resource will first be used from cache, and revalidated in background.
Local web server
I will continue using only local server for the rest of the post since the behavior is basically the same as CloudFlare. I will disable disk cache, however, such that it does not interfere with the service worker strategies. It's a bit confusing, but NetworkFirst strategy will actually forward request to the browser, and it might serve it from the cache if it is valid. Here is the full request workflow with a service worker:
Source: https://web.dev/service-worker-caching-and-http-caching/
NetworkFirst Strategy
Let's see the Network-first strategy in action first. We clean everything and load the page again with service worker:
Okay, service worker is handling our request, and since we don't have any cache, it loads it from the network. I now update the resource and refresh the page:
Okay, the service worker successfully loaded the resource from the network again.
StaleWhileRevalidate Strategy
I will now remove the NetworkFirst strategy handler and let the StaleWhileRevalidate handler process the request. Initial load:
I change the resource, and reload the page:
I see as if the service worker has already reloaded the resource, but it actually did it after it returned an existing cached version to the app. I can see it because I get the new translation string only after second reload: