Service workers zijn krachtig en absoluut de moeite waard om te leren. Hiermee kunt u een geheel nieuwe ervaring bieden aan uw gebruikers. Uw site kan direct worden geladen. Hij kan offline werken. Het kan worden geïnstalleerd als een native app en voelt net zo gepolijst, maar met het bereik en de vrijheid van het web.

Maar service workers zijn anders dan wat de meeste van ons web devs gewend zijn. Ze komen met een steile leercurve en een handvol addertjes onder het gras waar je voor moet oppassen.

Google Developers en ik hebben onlangs samengewerkt aan een project-Service Workies-een gratis spel voor het begrijpen van service workers. Tijdens het bouwen en het werken met de complexe ins en outs van service werkers, liep ik tegen een paar addertjes onder het gras aan. Wat me het meest hielp was het bedenken van een handvol metaforen. In deze post zullen we deze mentale modellen verkennen en onze hersenen om de paradoxale eigenschappen wikkelen die service workers zowel lastig als geweldig maken.

Hetzelfde, maar toch anders #

Tijdens het coderen van je service worker, zullen veel dingen vertrouwd aanvoelen. Je krijgt om je favoriete nieuwe JavaScript taal functies te gebruiken. Je luistert naar lifecycle events net als met UI events. Je beheert de control flow met promises zoals je gewend bent.

Maar ander gedrag van de service worker zorgt ervoor dat je je hoofd krabt in verwarring. Vooral als je de pagina vernieuwt en de veranderingen in je code niet toegepast ziet.

Een nieuwe laag #

Normaal gesproken heb je bij het bouwen van een site maar twee lagen om over na te denken: de client en de server. De service worker is een gloednieuwe laag die zich in het midden bevindt.

Denk aan je service worker als een soort browserextensie – een die je site kan installeren in de browser van je gebruiker. Eenmaal geïnstalleerd, breidt de service worker de browser voor uw site uit met een krachtige tussenlaag. Deze service worker laag kan alle verzoeken onderscheppen en afhandelen die uw site doet.

De service worker laag heeft zijn eigen levenscyclus, onafhankelijk van de browser tab. Een eenvoudige paginaverversing is niet genoeg om een service worker bij te werken – net zoals je niet zou verwachten dat een paginaverversing code bijwerkt die op een server is geïmplementeerd. Elke laag heeft zijn eigen unieke regels voor het bijwerken.

In het Service Workies-spel behandelen we de vele details van de levenscyclus van de service worker en geven we u veel praktijkervaring met het werken ermee.

Beschouw uw service worker als een nieuwe middelste laag met zijn eigen levenscyclus en methoden voor het bijwerken.

Krachtig, maar beperkt #

Het hebben van een service worker op uw site geeft u ongelooflijke voordelen. Uw site kan:

  • foutloos werken, zelfs als de gebruiker offline is
  • enorme prestatieverbeteringen krijgen door caching
  • pushmeldingen gebruiken
  • als PWA worden geïnstalleerd

Hoeveel service workers ook kunnen, ze zijn beperkt door hun ontwerp. Ze kunnen niets synchroon doen of in dezelfde thread als je site. Dat betekent dus geen toegang tot:

  • localStorage
  • the DOM
  • the window

Het goede nieuws is dat er een handvol manieren zijn waarop uw pagina met zijn servicewerker kan communiceren, waaronder directe postMessage, een-op-een Message Channels en een-op-veel Broadcast Channels.

Denk aan uw servicewerker als iets dat buiten uw pagina leeft. U kunt ermee praten, maar hij heeft geen directe toegang tot uw pagina.

Langlevend, maar kortlevend #

Een actieve service worker blijft bestaan, zelfs nadat een gebruiker uw site heeft verlaten of het tabblad heeft gesloten. De browser houdt deze service worker in de buurt, zodat hij klaar is als de gebruiker de volgende keer weer op je site komt. Voordat de allereerste aanvraag wordt gedaan, krijgt de service worker de kans om deze te onderscheppen en de controle over de pagina over te nemen. Hierdoor kan een site offline werken – de service worker kan een cache-versie van de pagina zelf serveren, zelfs als de gebruiker geen verbinding met internet heeft.

In Service Workies visualiseren we dit concept met Kolohe (een vriendelijke service worker) die requests onderschept en afhandelt.

Stopped #

Ondanks dat service workers onsterfelijk lijken te zijn, kunnen ze op bijna elk moment worden gestopt. De browser wil geen resources verspillen aan een service worker die op dat moment niets doet. Gestopt worden is niet hetzelfde als beëindigd worden – de service worker blijft geïnstalleerd en geactiveerd. Hij wordt alleen in slaapstand gezet. De volgende keer dat hij nodig is (bijv. om een verzoek af te handelen), maakt de browser hem weer wakker.

waitUntil #

Omdat het voortdurend mogelijk is om in slaap te worden gebracht, heeft je service worker een manier nodig om de browser te laten weten wanneer hij iets belangrijks aan het doen is en geen zin heeft om een dutje te doen. Dit is waar event.waitUntil() in het spel komt. Deze methode verlengt de levenscyclus waarin hij wordt gebruikt, en zorgt ervoor dat hij niet wordt gestopt en niet doorgaat naar de volgende fase van zijn levenscyclus totdat we klaar zijn. Dit geeft ons tijd om caches op te zetten, bronnen van het netwerk op te halen, etc.

Dit voorbeeld vertelt de browser dat onze service worker niet klaar is met installeren totdat de assets cache is aangemaakt en gevuld met de afbeelding van een zwaard:

self.addEventListener("install", event => {
event.waitUntil(
caches.open("assets").then(cache => {
return cache.addAll();
})
);
});

Watch out for global state #

Wanneer deze start/stop gebeurt, wordt het globale bereik van de service worker gereset. Dus wees voorzichtig geen global state te gebruiken in je service worker of je zult verdrietig zijn als hij de volgende keer weer wakker wordt en een andere state heeft dan wat hij verwachtte.

Overweeg dit voorbeeld dat een global state gebruikt:

const favoriteNumber = Math.random();
let hasHandledARequest = false;
self.addEventListener("fetch", event => {
console.log(favoriteNumber);
console.log(hasHandledARequest);
hasHandledARequest = true;
});

Op elk verzoek zal deze service worker een nummer loggen- laten we zeggen 0.13981866382421893. De variabele hasHandledARequest verandert ook in true. Nu blijft de service worker een tijdje inactief, dus de browser stopt hem. De volgende keer dat er een verzoek is, is de service worker weer nodig, dus de browser maakt hem wakker. Het script wordt opnieuw geëvalueerd. Nu is hasHandledARequest teruggezet naar false, en favoriteNumber is iets heel anders-0.5907281835659033.

Je kunt niet vertrouwen op opgeslagen toestand in een service worker. Ook kan het maken van instanties van dingen zoals Message Channels bugs veroorzaken: je krijgt een gloednieuwe instantie elke keer dat de service worker stopt/start.

Waarschuwing: Dit probleem is vooral belangrijk om in gedachten te houden wanneer u aan uw service worker-code werkt, omdat wanneer Chrome DevTools is geopend, het start/stop-gedrag is uitgeschakeld. Het is mogelijk dat u bugs die worden veroorzaakt door het vertrouwen op global state pas ziet wanneer ze naar uw gebruikers zijn verzonden.

In Service Workies hoofdstuk 3 visualiseren we onze gestopte service worker als iemand die alle kleur verliest terwijl hij wacht om te worden gewekt.

Denk aan uw service worker als een whippethond. Hij is snel, loyaal en geweldig. Hij blijft aan je zijde, wat er ook gebeurt. Maar meestal wil hij gewoon slapen. De hele tijd. Je moet hem laten weten wanneer je wilt dat hij wakker blijft. Brave hond!

Samen, maar toch apart #

Jouw pagina kan maar door één service worker tegelijk worden bestuurd. Maar er kunnen wel twee service workers tegelijk op geïnstalleerd zijn. Als je een wijziging aanbrengt in je service worker code en de pagina vernieuwt, ben je eigenlijk je service worker helemaal niet aan het bewerken. Service workers zijn onveranderlijk. In plaats daarvan maak je een geheel nieuwe. Deze nieuwe service worker (laten we hem SW2 noemen) zal installeren, maar nog niet activeren. Hij moet wachten tot de huidige service worker (SW1) is afgelopen (als je gebruiker je site verlaat).

Messing with another service worker’s caches #

Tijdens de installatie kan SW2 dingen instellen-zoals het maken en vullen van caches. Maar let op: deze nieuwe service worker heeft toegang tot alles waar de huidige service worker toegang tot heeft. Als je niet oppast, kan je nieuwe wachtende service worker de dingen voor je huidige service worker echt in de war schoppen. Enkele voorbeelden die je in de problemen kunnen brengen:

  • SW2 zou een cache kunnen verwijderen die SW1 actief gebruikt.
  • SW2 zou de inhoud van een cache kunnen bewerken die SW1 gebruikt, waardoor SW1 zou reageren met assets die de pagina niet verwacht.

Skip skipWaiting #

Een service worker kan ook de riskante skipWaiting() methode gebruiken om de controle over de pagina over te nemen, zodra hij klaar is met installeren. Dit is over het algemeen een slecht idee, tenzij je opzettelijk probeert om een buggy service worker te vervangen. De nieuwe service worker gebruikt misschien bijgewerkte bronnen die de huidige pagina niet verwacht, wat leidt tot fouten en bugs.

Start clean #

De manier om te voorkomen dat je service workers elkaar platgooien, is door ervoor te zorgen dat ze verschillende caches gebruiken. De eenvoudigste manier om dat te bereiken is door de cache-namen die ze gebruiken te versiëren.

const version = 1;
const assetCacheName = `assets-${version}`;
self.addEventListener("install", event => {
caches.open(assetCacheName).then(cache => {
// confidently do stuff with your very own cache
});
});

Wanneer je een nieuwe service worker inzet, bump je de version zodat deze doet wat hij nodig heeft met een geheel andere cache dan de vorige service worker.

End clean #

Zodra je service worker de activated status bereikt, weet je dat deze het heeft overgenomen, en dat de vorige service worker overbodig is (d.w.z.., niet langer nodig). Op dit punt is het belangrijk om de oude service worker op te ruimen. Niet alleen respecteert het de cache opslag limieten van je gebruikers, maar het kan ook onbedoelde bugs voorkomen.

De caches.match() methode is een vaak gebruikte snelkoppeling voor het ophalen van een item uit elke cache waar een match is. Maar het loopt door de caches in de volgorde waarin ze zijn gemaakt. Dus stel je hebt twee versies van een scriptbestand app.js in twee verschillende caches-assets-1 en assets-2. Uw pagina verwacht het nieuwere script dat is opgeslagen in assets-2. Maar als u de oude cache niet hebt verwijderd, zal caches.match('app.js') de oude van assets-1 retourneren en waarschijnlijk uw site breken.

Alles wat nodig is om op te ruimen na vorige service workers is het verwijderen van alle cache die de nieuwe service worker niet nodig heeft:

const version = 2;
const assetCacheName = `assets-${version}`;
self.addEventListener("activate", event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== assetCacheName){
return caches.delete(cacheName);
}
});
);
});
);
});

Voorkomen dat je service workers elkaar afknallen kost wat werk en discipline, maar is de moeite waard.

Denk aan de combinatie van je service worker en je site als een installeerbare app. Elke versie moet werken. Elke versie moet gescheiden zijn van de andere. Stel je eens voor hoe buggy een spel zou zijn als de ontwikkelaar per ongeluk een patch uitbracht die nieuwe spellogica gebruikte maar verouderde assets. Je zou zo snel woeden op de forums! Houd je app-versies & netjes.

Service worker mindset #

In de juiste mindset komen als je nadenkt over service workers zal je helpen de jouwe met vertrouwen op te bouwen. Als je ze eenmaal onder de knie hebt, kun je ongelooflijke ervaringen voor je gebruikers creëren.

Als je dit allemaal wilt begrijpen door een spel te spelen, dan heb je geluk! Ga Service Workies spelen waarin je de manieren van de service worker leert om de offline beesten te verslaan.

Laatst bijgewerkt: Jun 4, 2019 Artikel verbeteren

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.