Service Worker - KeynesYouDigIt/Knowledge GitHub Wiki
Big Ideas
- A particular serviceWorker runs completely independently of the page it works for- different process, and they can't see each other directly
- That means the page can't see its service workers'
console.log
s on the page - It doesn't have access to the DOM or anything in JS scope
- That means the page can't see its service workers'
- Service workers are primarily for intercepting, caching, and responding to fetch requests
- Can also manage push notifications
- Service workers are scoped (default scope is anything under the location of the file, can be narrowed to a particular range of API requests)
Gotchas
- They don't work in private tabs in Firefox
- Firefox allows you to inspect running service workers at
about:debugging
(scroll down) - "Fetch" events are much broader than just
fetch
, and also includes document navigation and static assets - Service workers only work over HTTPS (and localhost)
- Turning on an active service worker has a little bit of overhead, so don't run fetch requests through it if you don't have to
Registering a Service Worker
This is the only thing that actually goes in the app:
if ("serviceWorker" in navigator)(
window.addEventListener("load", () => {
navigator.serviceWorker.register("/service-worker.js").then(() => console.log("Registered!"))
})
)
Logging the registration is optional.
Service Worker Events
Install
Runs if the service worker has never been installed, or if the service worker it got is different than the active one
self.addEventListener("install", event => {
event.waitUntil(
// Preload anything for new cache
)
})
Activate
Runs after every tab using the old service worker is closed
self.addEventListener("activate", event => {
event.waitUntil(
self.skipWaiting() // Don't wait for other tabs to close
// Delete entire old cache
self.clients.claim() // Immediately turns on SW without waiting for a navigation event
)
})
Fetch
Runs on any network activity.
self.addEventListener("fetch", fetchEvent => {
fetchEvent.respondWith(someResponse) // Can stub one out with `new Response(body, options)`
})
Cache Methods
All Caches
caches.keys() // iterable list of caches for this domain
caches.open("some-cache") // Returns a promise with that cache
caches.delete("some-cache")
Deleting old caches
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => caches.delete(cacheName);
)
})
Working with a cache
Caching a response:
caches.open("some-cache")
.then(cache => {
cache.put(request, response.clone()) // .clone allows the response to pass unaltered back to the client
})
Finding a cached response:
caches.open("some-cache")
.then(cache => {
cache.match(someRequest) // matching response
cache.match(someUrl) // matching response
cache.match(/someUrlPattern/) // matching response
})
Precaching a response:
caches.open("some-cache")
.then(cache => {
cache.add(url) // make a request and cache its response
cache.addAll([url1, url2, url3]) // precache more than one
})
Client API
Available inside service workers, gets client objects so you can send them messages
self.clients.matchAll()
self.clients.get(id)
self.clients.openWindow(url) // Open the app
self.clients.claim() // Make an activated service worker the controller for a page, prevents having to navigate or refresh
Sending messages:
someClient.postMessage({
anythingYouWant: "Really",
})
Receiving messages on the client:
navigator.serviceWorker.addEventListener("message", event => {
console.log(event.data) // { anythingYouWant: "Really" }
})