Het is moeilijk om te weten wat servicemedewerkers doen zonder hun levenscyclus te begrijpen. Hun innerlijke werking zal ondoorzichtig en zelfs willekeurig lijken. Het helpt om te onthouden dat het gedrag van servicemedewerkers, net als bij elke andere browser-API, goed gedefinieerd en gespecificeerd is en offline applicaties mogelijk maakt, terwijl ook updates mogelijk worden gemaakt zonder de gebruikerservaring te verstoren.
Voordat u in Workbox duikt, is het belangrijk om de levenscyclus van servicemedewerkers te begrijpen, zodat wat Workbox doet zinvol is.
Termen definiëren
Voordat we ingaan op de levenscyclus van servicemedewerkers, is het de moeite waard enkele termen te definiëren over hoe die levenscyclus werkt.
Controle en reikwijdte
Het idee van controle is cruciaal om te begrijpen hoe servicemedewerkers werken. Een pagina die wordt beschreven als beheerd door een servicemedewerker, is een pagina waarmee een servicemedewerker namens hem netwerkverzoeken kan onderscheppen. De servicemedewerker is aanwezig en kan binnen een bepaald bereik werkzaamheden voor de pagina uitvoeren.
Domein
De reikwijdte van een servicemedewerker wordt bepaald door zijn locatie op een webserver. Als een servicemedewerker een pagina opent die zich bevindt op /subdir/index.html
en zich bevindt op /subdir/sw.js
, is het bereik van de servicemedewerker /subdir/
. Bekijk dit voorbeeld om het concept van bereik in actie te zien:
- Navigeer naar https://service-worker-scope-viewer.glitch.me/subdir/index.html . Er verschijnt een bericht waarin staat dat geen enkele servicemedewerker de pagina beheert. Die pagina registreert echter een servicemedewerker van
https://service-worker-scope-viewer.glitch.me/subdir/sw.js
. - Laad de pagina opnieuw. Omdat de servicemedewerker is geregistreerd en nu actief is, beheert hij de pagina. Er zal een formulier zichtbaar zijn met het bereik, de huidige status en de URL van de servicemedewerker. Let op: het opnieuw laden van de pagina heeft niets te maken met de reikwijdte, maar eerder met de levenscyclus van de servicemedewerker, die later zal worden uitgelegd.
- Navigeer nu naar https://service-worker-scope-viewer.glitch.me/index.html . Hoewel er op deze oorsprong een servicemedewerker is geregistreerd, verschijnt er nog steeds een bericht dat er momenteel geen servicemedewerker is. Dat komt omdat deze pagina niet binnen het bereik van de geregistreerde servicemedewerker valt.
Het bereik beperkt welke pagina's de servicemedewerker beheert . In dit voorbeeld betekent dit dat de servicemedewerker die wordt geladen vanuit /subdir/sw.js
alleen pagina's kan beheren die zich in /subdir/
of de substructuur ervan bevinden.
Het bovenstaande is hoe scoping standaard werkt, maar het maximaal toegestane bereik kan worden overschreven door de Service-Worker-Allowed
antwoordheader in te stellen en een scope
door te geven aan de register
.
Tenzij er een zeer goede reden is om het bereik van de servicemedewerker te beperken tot een subset van een oorsprong, laadt u een servicemedewerker vanuit de hoofdmap van de webserver, zodat de reikwijdte ervan zo breed mogelijk is, en hoeft u zich geen zorgen te maken over de Service-Worker-Allowed
koptekst. Zo is het voor iedereen een stuk eenvoudiger.
Cliënt
Als er wordt gezegd dat een servicemedewerker een pagina beheert, bestuurt hij in werkelijkheid een klant. Een client is elke geopende pagina waarvan de URL binnen het bereik van die servicemedewerker valt. Concreet zijn dit exemplaren van een WindowClient
.
De levenscyclus van een nieuwe servicemedewerker
Voordat een servicemedewerker een pagina kan beheren, moet deze als het ware eerst tot stand worden gebracht. Laten we beginnen met wat er gebeurt als een gloednieuwe servicemedewerker wordt ingezet voor een website zonder actieve servicemedewerker.
Registratie
Registratie is de eerste stap in de levenscyclus van servicemedewerkers:
<!-- In index.html, for example: -->
<script>
// Don't register the service worker
// until the page has fully loaded
window.addEventListener('load', () => {
// Is service worker available?
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('Service worker registered!');
}).catch((error) => {
console.warn('Error registering service worker:');
console.warn(error);
});
}
});
</script>
Deze code draait op de hoofdthread en doet het volgende:
- Omdat het eerste bezoek van de gebruiker aan een website plaatsvindt zonder een geregistreerde servicemedewerker, moet u wachten tot de pagina volledig is geladen voordat u zich registreert. Dit voorkomt bandbreedteconflicten als de servicemedewerker iets vooraf in de cache opslaat.
- Hoewel de servicemedewerker goed wordt ondersteund , helpt een snelle controle fouten te voorkomen in browsers waarin deze niet wordt ondersteund.
- Wanneer de pagina volledig is geladen en als servicemedewerker wordt ondersteund, registreert u
/sw.js
.
Enkele belangrijke dingen die u moet begrijpen zijn:
- Servicemedewerkers zijn alleen beschikbaar via HTTPS of localhost .
- Als de inhoud van een servicemedewerker syntaxisfouten bevat, mislukt de registratie en wordt de servicemedewerker verwijderd.
- Let op: servicemedewerkers werken binnen een bepaalde scope. Hier is het bereik de volledige oorsprong, zoals deze vanuit de hoofdmap is geladen.
- Wanneer de registratie begint, wordt de status van de servicemedewerker ingesteld op
'installing'
.
Zodra de registratie is voltooid, begint de installatie.
Installatie
Een servicemedewerker activeert de install
na registratie. install
wordt slechts één keer per servicemedewerker aangeroepen en wordt pas opnieuw geactiveerd nadat deze is bijgewerkt. Een callback voor de install
kan worden geregistreerd in het bereik van de werker met addEventListener
:
// /sw.js
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v1';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v1'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.bc7b80b7.css',
'/css/home.fe5d0b23.css',
'/js/home.d3cc4ba4.js',
'/js/jquery.43ca4933.js'
]);
}));
});
Hierdoor wordt een nieuwe Cache
instantie gemaakt en worden activa vooraf in de cache geplaatst. We hebben later genoeg mogelijkheden om over precaching te praten, dus laten we ons concentreren op de rol van event.waitUntil
. event.waitUntil
accepteert een belofte en wacht totdat die belofte is opgelost. In dit voorbeeld doet die belofte twee asynchrone dingen:
- Creëert een nieuwe
Cache
instantie met de naam'MyFancyCache_v1'
. - Nadat de cache is gemaakt, wordt een reeks asset-URL's vooraf in de cache geplaatst met behulp van de asynchrone
addAll
methode .
De installatie mislukt als de belofte(n) die zijn doorgegeven aan event.waitUntil
worden afgewezen . Als dit gebeurt, wordt de servicemedewerker weggegooid.
Als de beloften worden opgelost , slaagt de installatie en verandert de status van de servicemedewerker in 'installed'
en wordt deze vervolgens geactiveerd.
Activering
Als de registratie en installatie slagen, wordt de servicemedewerker geactiveerd en wordt de status 'activating'
Er kan tijdens de activering werk worden verricht in de activate
van de servicemedewerker. Een typische taak bij dit evenement is het opschonen van oude caches, maar voor een gloednieuwe servicemedewerker is dit op dit moment niet relevant en zal worden uitgebreid als we het hebben over de updates van servicemedewerkers.
Voor nieuwe servicemedewerkers: activate
de branden onmiddellijk nadat install
is voltooid. Zodra de activering is voltooid, wordt de status van de servicemedewerker 'activated'
. Merk op dat de nieuwe servicemedewerker standaard pas begint met het beheren van de pagina bij de volgende navigatie of paginavernieuwing.
Updates van servicemedewerkers verwerken
Zodra de eerste servicemedewerker is ingezet, moet deze waarschijnlijk later worden bijgewerkt. Er kan bijvoorbeeld een update nodig zijn als er wijzigingen optreden in de verwerking van aanvragen of in de precaching-logica.
Wanneer er updates plaatsvinden
Browsers controleren op updates voor een servicemedewerker wanneer:
- De gebruiker navigeert naar een pagina binnen het bereik van de servicemedewerker.
-
navigator.serviceWorker.register()
wordt aangeroepen met een URL die verschilt van de momenteel geïnstalleerde servicemedewerker , maar wijzig de URL van een servicemedewerker niet ! -
navigator.serviceWorker.register()
wordt aangeroepen met dezelfde URL als de geïnstalleerde servicewerker, maar met een ander bereik. Nogmaals, vermijd dit door, indien mogelijk, de reikwijdte aan de basis van een oorsprong te houden. - Wanneer gebeurtenissen zoals
'push'
of'sync'
in de afgelopen 24 uur zijn geactiveerd, maar u zich nog geen zorgen hoeft te maken over deze gebeurtenissen.
Hoe updates plaatsvinden
Weten wanneer de browser een servicemedewerker bijwerkt is belangrijk, maar dat geldt ook voor het ‘hoe’. Ervan uitgaande dat de URL of het bereik van een servicemedewerker ongewijzigd is, wordt een momenteel geïnstalleerde servicemedewerker alleen bijgewerkt naar een nieuwe versie als de inhoud ervan is gewijzigd.
Browsers detecteren veranderingen op een aantal manieren:
- Eventuele byte-voor-byte wijzigingen in scripts die zijn aangevraagd door
importScripts
, indien van toepassing. - Eventuele wijzigingen in de code op het hoogste niveau van de servicemedewerker, die van invloed zijn op de vingerafdruk die de browser ervan heeft gegenereerd.
De browser doet hier veel zwaar werk. Om ervoor te zorgen dat de browser alles heeft wat nodig is om op betrouwbare wijze wijzigingen in de inhoud van een servicemedewerker te detecteren, mag u de HTTP-cache niet vertellen deze vast te houden en mag u de bestandsnaam niet wijzigen. De browser voert automatisch updatecontroles uit wanneer er een navigatie naar een nieuwe pagina binnen het bereik van een servicemedewerker plaatsvindt.
Handmatige activering van updatecontroles
Wat updates betreft, zou de registratielogica over het algemeen niet moeten veranderen. Toch kan er één uitzondering zijn als sessies op een website een lange levensduur hebben. Dit kan gebeuren in applicaties met één pagina waar navigatieverzoeken zeldzaam zijn, omdat de applicatie doorgaans één navigatieverzoek tegenkomt aan het begin van de levenscyclus van de applicatie. In dergelijke situaties kan een handmatige update worden geactiveerd op de hoofdthread:
navigator.serviceWorker.ready.then((registration) => {
registration.update();
});
Voor traditionele websites, of in ieder geval waar gebruikerssessies geen lange levensduur hebben, is het activeren van handmatige updates waarschijnlijk niet nodig.
Installatie
Wanneer u een bundelaar gebruikt om statische assets te genereren, bevatten deze assets hashes in hun naam, zoals framework.3defa9d2.js
. Stel dat sommige van deze assets later in de cache worden opgeslagen voor offline toegang. Hiervoor is een update van een servicemedewerker nodig om bijgewerkte assets vooraf in de cache op te slaan:
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v2';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v2'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.ced4aef2.css',
'/css/home.cbe409ad.css',
'/js/home.109defa4.js',
'/js/jquery.38caf32d.js'
]);
}));
});
Er zijn twee dingen anders dan het voorbeeld van de eerste install
van eerder:
- Er wordt een nieuwe
Cache
instantie met de sleutel'MyFancyCacheName_v2'
gemaakt. - De vooraf in de cache opgeslagen itemnamen zijn gewijzigd.
Een ding om op te merken is dat er naast de vorige een bijgewerkte servicemedewerker wordt geïnstalleerd. Dit betekent dat de oude servicemedewerker nog steeds de controle heeft over alle geopende pagina's, en dat de nieuwe na installatie in een wachtstatus komt totdat deze wordt geactiveerd.
Standaard wordt een nieuwe servicemedewerker geactiveerd als er geen klanten meer worden beheerd door de oude. Dit gebeurt wanneer alle geopende tabbladen voor de betreffende website gesloten zijn.
Activering
Wanneer een bijgewerkte servicemedewerker wordt geïnstalleerd en de wachtfase eindigt, wordt deze geactiveerd en wordt de oude servicemedewerker verwijderd. Een veel voorkomende taak die moet worden uitgevoerd in de activate
van een bijgewerkte servicemedewerker is het opschonen van oude caches. Verwijder oude caches door de sleutels voor alle open Cache
instanties op te halen met caches.keys
en caches te verwijderen die niet in een gedefinieerde acceptatielijst staan met caches.delete
:
self.addEventListener('activate', (event) => {
// Specify allowed cache keys
const cacheAllowList = ['MyFancyCacheName_v2'];
// Get all the currently active `Cache` instances.
event.waitUntil(caches.keys().then((keys) => {
// Delete all caches that aren't in the allow list:
return Promise.all(keys.map((key) => {
if (!cacheAllowList.includes(key)) {
return caches.delete(key);
}
}));
}));
});
Oude caches ruimen zichzelf niet op. We moeten dat zelf doen, anders lopen we het risico de opslagquota te overschrijden. Omdat 'MyFancyCacheName_v1'
van de eerste servicewerker verouderd is, wordt de lijst met toegestane caches bijgewerkt om 'MyFancyCacheName_v2'
op te geven, waardoor caches met een andere naam worden verwijderd.
De activate
eindigt nadat de oude cache is verwijderd. Op dit punt zal de nieuwe servicemedewerker de controle over de pagina overnemen en uiteindelijk de oude vervangen!
De levenscyclus gaat altijd door
Of Workbox nu wordt gebruikt voor de implementatie en updates van servicemedewerkers, of dat de Service Worker API rechtstreeks wordt gebruikt, het loont de moeite om de levenscyclus van servicemedewerkers te begrijpen. Met dat inzicht zou het gedrag van servicemedewerkers eerder logisch dan mysterieus moeten lijken.
Voor degenen die geïnteresseerd zijn in een diepere duik in dit onderwerp, is het de moeite waard om dit artikel van Jake Archibald te lezen. Er zit heel veel nuance in de manier waarop de hele dans rond de servicelevenscyclus verloopt, maar het is kenbaar, en die kennis zal ver reiken bij het gebruik van Workbox.