💾 Setting Up a Service Worker+v0.6
A Service Worker is a super powerful browser API that allows you to intercept network requests online or offline. Hydrogen is now able to expose all the generated routes to your Service Worker so that you can do some cool precaching of your routes or whatever fits your use case.
Basic Setup
Create a simple Service Worker file in the root of your project.
sw.js
self.addEventListener('install', (e) => {
});
Register Service Worker in a layout
default.js
module.exports = () => `
<script>
const registerSW = async () => {
if (!navigator.serviceWorker) {
return false;
}
const reg = await navigator.serviceWorker.register('/sw.js');
await reg.update();
}
registerSW();
</script>
`;
Add the Service Worker file to the config file.
hydrogen.config.js
module.exports = {
sw: 'sw.js',
};
Run npx hydrogen build
and the sw.js
will be copied to the dist folder
Exposing page routes to your Service Worker
Here is our pages folder structure.
/pages
|_ javascript
|_ index.js
|_ functions.js
|_ oop
|_ index.js
|_ java
|_ index.js
|_ index.js
Hydrogen generates the above folder structure into an array of routes like the example below.
const routes = [
{
"route": "/",
"filename": "index.html",
"index": true,
"depth": 0
},
{
"route": "/java",
"filename": "index.html",
"index": true,
"depth": 1
},
{
"route": "/javascript",
"filename": "index.html",
"index": true,
"depth": 1
},
{
"route": "/javascript",
"filename": "functions.html"
"index": false,
"depth": 1
},
{
"route": "/javascript/oop",
"filename": "index.html",
"index": true,
"depth": 2
}
]
Your sw.js
now has access to the above const routes
, so you can do something like creating a simple precache.
You also have access to the const DEV
which determines the mode
self.addEventListener('install', (e) => {
e.waitUntil(caches.open('data').then(cache => {
return cache.addAll(routes.map(({ route }) => route));
}));
});
Versioning your Service Worker cache+v0.9
Managing cache is not the easiest thing in the world, especially if you need to invalidate the cache.
You can now get access to the const CACHE_VERSION
variable
which is determined by the version field in your package.json
file
const removeOldCaches = async () => {
const cacheVersions = await caches.keys();
const handler = () => Promise.all(cacheVersions.map((cacheName) => caches.delete(cacheName)));
if (cacheVersions.includes(CACHE_VERSION)) {
cacheVersions.splice(cacheVersions.indexOf(CACHE_VERSION), 1)
}
return handler();
};
self.addEventListener('install', (event) => {
event.waitUntil(removeOldCaches())
})
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.open(CACHE_VERSION).then((cache) => {
if (event.request.destination !== 'image') {
return fetch(event.request);
}
return cache.match(event.request).then((cacheResponse) => {
return cacheResponse || fetch(event.request).then((networkResponse) => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
});
}),
);
});
Here we are deleting our old caches if we deployed a new version of our app,
it will only delete the old cache versions if the new cache version was successfully created.
If any of the promises fails in e.waitUntil
then the entire install event will cancel