Easy Service Worker for SPA Caching And Update

Documentation

About

GitHub repository

Available on github.com

Contents provided by structured lists

Available on ulrichlehmann.de

Motivation

For my PWA projects I spent a lot of time reading docs, evaluating examples and derived own concepts to implement a Service Worker, which enables SPA caching and obviously also updates. After some time I realized that update capability is more tricky than reflected in the information and examples I found. But having gathered some knowledge I decided to create a solution from scratch with an unconventional approach, featuring messaging between a starter script in the index file and the SW script in the usual SW file.

Demo and evaluation PWA

For sharing my solution I built this demo PWA to provide a platform for evaluation and created the GitHub repository to provide the code files. You can download the ZIP-file, rename the extracted site root folder to "EasyServiceWorker", check the configuration, and the PWA is ready to go after you start a local server serving that folder. And you can open the project in your editor.

The demo PWA as finished basis for copying or derived projects

If you want to use my code, read the MIT license and feel free to utilize it, and probably spare yourself a lot of time and nerves. Further development of the basis PWA is not intended (no pull requests enabled), but it is ideal for clones w/ more Service Worker functionalities. Though there is no obligation to do so, an informal connection to derived projects would be helpful for me and others.

Service Worker caching and update of the demo PWA

Problems w/ regular browser caching in general

Regular browser caching is obscure and there is no reliable mechanism implemented, that detects file changes as such for fetching updated files from servers. To enforce this behavior it is best practice to version file or folder names for targeted requests. On the other hand, restoring a website offline from its regular cache is not an implemented basic feature. Here comes the Service Worker interface into play.

A SW script is always requested from the server and not cached.

As default the received file is evaluated and a changed SW script is always recognized.

Service Worker registration call on every website loading

It is best practice to do a SW registration call on every website loading. If no Service Worker is installed, an installation including cache writing is done. If a Service Worker is already installed, no redundant action takes place and therefore no performance is lost.

Updating a site by replacing its Service Worker cache

To update SW cached files the best practice is to replace the deprecated cache by changing the cache key for the new cache and removal of the deprecated cache.

Pitfalls on updating a site by only replacing the SW cache

The initially loaded but deprecated index file resides in the new cache again. The browser window content does not change. The updated content is displayed not until the next site loading.

Service Worker cache processing in general

A Service Worker enables the storage of file contents in a dedicated browser cache. On requests the browser can be forced to fetch this cached contents instead of regular fetching (server or standard browser cache).

Strategies for delivering requested file contents

If a Service Worker is installed, several strategies for fetching requested file contents are possible.

Service Worker cache update processing on a triggered event

An already installed Service Worker detects a changed SW script on the initial website request. Triggered by a detected change a cache is written named by a given cache key. An update event is triggered, which can be handled with further processing.

Service Worker cache processing of the demo PWA

Later the processing is described again, but from a different perspective.

Strategy for delivering requested file contents

In my demo PWA files are only fetched from the server once on filling a named SW cache, and otherwise fetched from cache immediately. Notice that the cache is local and the contents are delivered literally immediately, whereas server content delivery is delayed by network transfer.

Registration on a new or prior unregistered Service Worker

The cache is filled or overwritten, followed by the SW activation, which enables messaging, followed by a version value comparison and finally the main script file is loaded.

Caching, update, and request handling

As mentioned before, the cached contents are delivered immediately and a version value check is done between the index and the SW script, both still in the old version, followed by the display of the old app version. Delayed the server response of the potentially changed SW script file is evaluated.

No SW script change is detected.

Because no change is detected the old contents are still up-to-date and no further processing is needed.

A SW script change is detected.

Meanwhile the app is displayed. Due to the change, by default the defined files are requested from the server and cached. After the new cache is written, the deprecated caches are removed (should be one). Distinctive versioning is therefore paramount. Important: Notice that the initially loaded but deprecated index file resides in the new cache again. To fix this, the Service Worker is unregistered after being activated and the window is reloaded to start w/ initial processing, as described before, including overriding the flawed cache.

Important general points

Service Workers do not run on the UI-thread.

Running on a different thread the SW script has a restricted scope. I have not evaluated ways to overcome this restriction, because in my opinion it makes sense and is actually helpful for understanding the mechanism.

Scope of the SW script

Keep in mind, that the SW script has no direct access to starter and main script variables and methods and vice versa.

Console logging statements in the SW script

Console output from the Service Worker script is not available as usual. Instead you can utilize the already implemented messaging feature.

Console logging lines order during update processing

The logged console lines are not in the expected order during the update processing, especially for the triggered reload.

Service Workers run for defined website scopes.

It is possible to run several Service Workers on a website by placing them in different folders and defining corresponding scopes. It is important to be aware, that Service Workers are always registered on the domain level and must be differentiated.

Service Workers are always registered on the domain level.

Registration, cache management and messaging of Service Workers take place on the domain root level independent of the scope configuration. Interference between multiple Service Workers on a website can only be avoided by individual script file names and different cache keys. To be prepared for additional Service Workers on a website in the future, it is paramount from the start to follow an open but restricting naming concept.

Keep the SW script file name and the defined version string length.

After the first Service Worker installation both are used for identifying and differentiation and the version string is also used as the cache key. Changing breaks the update processing and can lead to interference, if several Service Workers are installed.

Use a concept for SW script file names and the cache keys.

The naming concept implemented in the demo app includes the use of a specific script related acronym for the file name and a specific script related key string length for the cache key.

Using the HTTP-protocol Service Workers cannot be installed.

But by design it is possible to use the specific domain "localhost" w/o an otherwise needed local server HTTPS-configuration. My demo app is configured for "localhost:8080".

Messaging is only applicable on an activated Service Worker.

Important code specific points

Evaluate updates always on your local machine before publishing.

The introduced Service Worker implementation is very reliable, but unforgiving on some mistakes, e.g. an accidentally unequal versioning, which can break the implemented update processing and even the loading of the app by permanent loops. Always evaluate thoroughly on your local machine with the browser cache under your control before publishing.

You might choose an alternative browser.

The demo PWA runs in "The Big Three" web browsers. You might choose an alternative to your mainly used browser for clearing all cached data w/o hesitation, if an error occurs. If everything is fine and running you can switch back anyways.

The cache key string length must be set in the SW script variable.

For the cache removal during an update the cache key length value is used as a condition.

Avoid reuse of version strings.

Best practice for versioning is to use ascending numbers. A reused version string might still be used in a user's browser, because no intermediate update has taken place.

File paths for caching

For filling the cache the relative paths are to be set in an array variable of the SW script. Each cached file is referenced by its path relative to the domain root URL, which resembles the regular fetch argument.

Straightforward solution

The app's SPA architecture is ideal for using a Service Worker.

The demo PWA has a simplified client-side building Single Page App (SPA) architecture. Typically an SPA requires only a small number of files in a flat structure to be managed for caching. Notice that Service Workers are installable w/o any PWA functionality.

Index file

The SPA main script and the SW script are connected by a starter script, which is integrated in the index file mark-up. The Service Worker is registered there, and at the end of the following SW-specific processing, the main script file loading is triggered there. The PWA manifest file is loaded by tag.

Service Worker file

The SW file contains the code to handle the Service Worker life cycle and manage its cache.

Main SPA script file

The main script contains the code for building the app's main content and UI. Its file loading is triggered by the starter script. Because the application of CustomElements is common in SPA scenarios, a non-functional CustomElement is registered by the main script for evaluating pitfalls, e.g. interference caused by messaging from other Service Workers.

Manifest file

The standard file contains the information necessary for installing a SPA as a PWA.

SVG file for PWA icons

Also serves as favicon.

Index CSS file

The file can also be loaded as text for inserting into a ShadowRoot or a CustomElement placed in a ShadowRoot.

Equal versioning of the update-involved files ensures reliability.

The starter script compares the messaged SW script version.

The starter script triggers a reload as part of an update.

The versioned main script file loading is triggered by code.

For an update the old Service Worker is explicit unregistered.

The unregistering ensures, that the complete cache is up-to-date afterwards after being overwritten, including the on the first writing loaded deprecated index file.

An update is finished by applying the newly cached files.

After the deprecated app UI was loaded initially, it is replaced by the updated app.

Injection of SW variable values into the main script w/o update

The version messaging at the start can be used to transfer changeable values from the SW script into the main script w/o an update needed. In the demo PWA a value is only written into the local storage by the index script to show the concept.

Code review and evaluation based on console logging

Development console logging

Console logging line statements are included in the starter script at important processing steps. Make sure that logging is preserved, because the update processing includes a browser reload. You can analyze different scenarios as described next for a deeper understanding.

Single SPA instance processing

You run the demo PWA in one window for evaluating the basic processing for each Service Worker action.

Multi SPA instances processing

You run the demo PWA in two windows for evaluating the basic processing for each Service Worker action and the mutual impact on processing for each window.

Multi Service Worker processing

You start the demo PWA in one window for evaluating the basic processing for each Service Worker action, while a different Service Worker registered on the same domain level is loaded in another window. As a mock-up you can implement the demo PWA as described, but in a different folder w/ different names and values.

Implementation

ZIP-file download

You can download the ZIP-file, unpack it in a folder named "EasyServiceWorker", check the configuration, and the PWA is ready to go after you start a local server serving the files of that folder.

General configuration (URL and scope)

1 JS script in index.html

const serviceWorkerScope = VALUE

2 JS script in index.html

return navigator.serviceWorker.register(VALUE)

3.1 Manifest file (internet)

"start_url": VALUE
Because outcommenting of JSON data lines is not possible like for JS, there are two manifest files and the file extension of the appropriate file must be named "webmanifest".

3.2 Manifest file (localhost)

"start_url": VALUE
Because outcommenting of JSON data lines is not possible like for JS, there are two manifest files and the file extension of the appropriate file must be named "webmanifest".

4 Service Worker script file

const relativeUrlsArray = VALUE

5 Versioning settings

Also versioning values must be set as described in the update configuration section.

Update configuration (versioning)

1 index.html

<title>Easy Service Worker Demo PWA#VALUE</title>

2 Service Worker script file

const swScriptVersion = VALUE
const cacheKeyLength = VALUE

3 File name of the main script file

main.VALUE.js

Value injection configuration

Service Worker script file

const injectableValueA = VALUE

First run

Local development server

For the HTTP-protocol a Service Worker only runs for "localhost".
Make sure that the URL port set in the general configuration and the server's actual port are identical.

Browser

You might choose an alternative to your mainly used browser for clearing all cached data w/o hesitation, if a persistent error occurs. If everything is fine and running you can switch back anyways.

Developer tools console

Open the console to follow the processing steps. Because of reloads make sure that logging preservation is enabled.

Before publishing

The introduced Service Worker implementation is very reliable, but unforgiving on some mistakes, e.g. accidentally unequal versioning, which breaks the update processing and can lead to permanent loops. Always evaluate thoroughly on your local machine with the browser cache under your control before publishing.