Reference
Compressed sheet for the first service worker path. Normative depth: Archibald lifecycle, MDN register().
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker
.register("/sw.js") // URL relative to origin, not the current page file
.then((reg) => console.log("registered", reg.scope))
.catch((err) => console.error("SW register failed", err));
});
}
"serviceWorker" in navigator.
window load so install/precache does not
fight first paint bandwidth
(registration article).
register() with the same script URL is effectively a
no-op for control (update checks still happen per browser rules).
./ relative to the
service worker script URL.
/app/sw.js → default scope
/app/ (not the whole origin unless the script lives at
the root or you set options carefully).
register(url, { scope: "/app/" }) — scope
cannot escape max allowed path rules (script location still bounds
what you can claim).
Browser downloads, parses, and executes the SW script.
First event. Precache with
event.waitUntil(promise). Fail the promise → SW
discarded; never controls clients.
Ready for functional events. Cleanup old caches here on updates (later lesson).
By default the page that first registered is
not controlled until a later navigation/refresh.
navigator.serviceWorker.controller may be
null on first visit.
// sw.js — ServiceWorkerGlobalScope (self)
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open("static-v1").then((cache) => cache.addAll(["/", "/app.js"]))
);
});
self.addEventListener("activate", (event) => {
// first SW: often empty; updates use this for cache cleanup
});
// Optional: take control of already-open pages (timing-sensitive)
// self.addEventListener("activate", (event) => {
// event.waitUntil(self.clients.claim());
// });
| Concept | Means |
|---|---|
| Registered | Browser is managing this script/scope |
| Installed |
install succeeded (deps like precache OK)
|
| Activated | Can handle functional events when controlling |
| Controlling this page |
navigator.serviceWorker.controller !== null — fetches
go through SW
|