Take a look at Service workers
Service Worker Introduction and precautions
A Service Worker is a script that the browser runs in the background independently of the web page, opening the door to functionality that does not require web pages or user interaction. They now include features like push notifications and background synchronization. In the future, Service workers will support other features such as periodic synchronization or geo-fencing.
Precautions for Use
- It is a JavaScript Worker with no direct access to the DOM. The Service Worker communicates with the pages it controls by responding to messages sent by the postMessage interface, which can perform operations on the DOM if necessary.
- The Service Worker is a programmable network agent that controls how network requests sent by pages are handled.
- During development, you can use the Service Worker through localhost, but if you want to deploy the Service Worker on the site, you need to set HTTPS on the server.
- The Service Worker is aborted when not in use and restarted the next time it is needed, so global state in the Service Worker onFetch and onMessage handlers cannot be relied on. If there is information that needs to be kept continuously and reused after a restart, the Service Worker can access the IndexedDB API.
Let’s start with an overview of the Service Wroker lifecycle
Introduction to life Cycle
The life cycle of a Service Worker is completely independent of the web page.
Installation: During installation, some static assets usually need to be cached, and if some resources have been successfully cached, the Service Worker is installed. If any files fail to download or cache, then the installation step will fail and the Service Worker will not be activated (that is, not installed). If that happens, don’t worry, it will try again next time.
Activation: After a successful installation, the activation step is followed, and the old cache is usually managed at this stage, which is described in more detail below. Page control: The Service Worker exercises control over all pages in its scope, but the pages that register the Service Worker for the first time need to be loaded again before they are under its control. After the service worker thread exercises control, it will be in one of two states: the service worker thread terminates to save memory, or processes fetch and message events, which will occur after a network request or message is issued from the page.
The following is a simplified lifecycle for the initial installation of the Service Worker:
Let’s start with the first installation, which goes through the following process:
First installation and use
Check the Service Worker usage mode first
Implementation process
- The Install event is the first event that the Service Worker gets and only happens once.
- A promise passed to installevent.waituntil () indicates the duration of the installation and whether it was successful.
- The Service Worker does not receive events such as fetch and push until the installation has successfully completed and is in an “active state.”
- By default, pages are not fetched through the Service Worker, unless the page request itself requires the Service Worker to be executed. Therefore, the page needs to be refreshed to see the impact of the Service Worker.
- Clients.claim () replaces this default value and controls uncontrolled pages ahead of time.
We register the service worker with the following script and dynamically add an image 3 seconds later
<script>
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered! ', reg))
.catch(err => console.log('Boo! ', err));
setTimeout(() => {
const img = new Image();
img.src = '/dog.svg';
document.body.appendChild(img);
}, 3000);
</script>
Copy the code
The Service Worker(sw.js) code looks like this:
self.addEventListener('install', event => {
console.log('V1 installing... ');
event.waitUntil(
caches.open('static-v1').then(cache => cache.add('/cat.svg'))); }); self.addEventListener('activate', event => {
console.log('V1 now ready to handle fetches! ');
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.origin == location.origin && url.pathname == '/dog.svg') {
event.respondWith(caches.match('/cat.svg')); }});Copy the code
The service worker caches an image of a cat at installation time and provides it when requesting /dog.svg, but when you run the example, you see a puppy image when it is first loaded and a cat image after a refresh.
Scope and control
The default scope for Service Worker registration is./ relative to the script url. This means that if a Service Worker is registered at //example.com/foo/bar.js, its default scope is //example.com/foo/. Through the navigator. ServiceWorker. Controller (whose will is null or a Service Worker instance) to test whether the client control.
Download, parse, and execute
The first Service Worker is downloaded when.register() is called. If the script fails to download, parse, or raise an error during initial execution, the registrar Promise will reject and discard the Service Worker.
DevTools for Chrome displays this error in the console and in the Service Worker section of the app TAB:
Install Service Worker
The first event to get is Install. This event is triggered immediately when the Worker executes, and it can only be called once per Service Worker. If you change the Service Worker script, the browser treats it as a different Service Worker and it gets its own install event. The update will be described in more detail later.
Everything you need can be cached in the Install event until you can control the client. Event.waituntil () waits for the promise to complete and then determines whether the installation was successful. If the promise refuses, the installation failed and the browser dismisses the Service Worker. It will have no control over the client. This means that we cannot rely on the presence of “cat.svg” in the cache of the FETCH event. It is a dependency.
Activate
When the Service Worker is ready to control the client and handle function events like push and sync, it gets an Activate event. This does not mean that the page that calls.register() will be controlled. As shown in the example above, even if the Service Worker requests dog.svg long after it has been activated, it does not process the request and still sees the image of the puppy. The default value is consistency.
clients.claim
After activating the Service Worker, you can lift control of uncontrolled pages by calling clients.claim(). You can see this in action in this example, where you clear the site’s cached data first
The specific code is as follows
self.addEventListener('install', event => {
console.log('Installing... ');
event.waitUntil(
caches.open('static-v1').then(cache => cache.add('cat.svg'))); }); self.addEventListener('activate', event => {
clients.claim();
console.log('Now ready to handle fetches! ');
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.origin == location.origin && url.pathname.endsWith('/dog.svg')) {
event.respondWith(caches.match('cat.svg')); }});Copy the code
Update Service Worker
Trigger update timing
- Navigate to a scoped page.
- Update functionality events such as push and sync unless an update check has been made within the previous 24 hours.
- Call.register() only if the Service Worker url has changed.
Most browsers ignore the cache header by default when checking for updates to registered Service Worker scripts.
The update process
- If the bytes of the Service Worker are different from those already in the browser, consider updating the Service Worker.
- The updated Service Worker starts with the existing Service Worker and gets its own install event.
- If the new Worker has an abnormal status code (for example, 404), fails to parse, raises an error during execution, or is rejected during installation, the system will abandon the new Worker, but the current Worker is still active.
- After the installation is successful, the updated Worker will be in a waiting state until the existing Worker cedes authorization. (Note that clients overlap during the refresh.)
- Self.skipwaiting () skips the wait and the Service Worker is activated immediately after installation.
The sample
Let’s update the example above to respond with a picture of a horse instead of a picture of a cat.
const expectedCaches = ['static-v2'];
self.addEventListener('install', event => {
console.log('V2 installing... ');
event.waitUntil(
caches.open('static-v2').then(cache => cache.add('/horse.svg'))); }); self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys => Promise.all(
keys.map(key => {
if(! expectedCaches.includes(key)) {return caches.delete(key);
}
})
)).then(() => {
console.log('V2 now ready to handle fetches! '); })); }); self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.origin == location.origin && url.pathname == '/dog.svg') {
event.respondWith(caches.match('/horse.svg')); }});Copy the code
Looking at the examples above, you should still see pictures of cats, for reasons we’ll see below
The update process is as follows:
Install
I have changed the cache name from static-v1 to static-v2. This means I can set up a new cache without overwriting the contents of the current cache that the old Service Worker is still using.
Waiting
After the Service Worker is successfully installed, the updated Service Worker is delayed until the existing Service Worker no longer controls any clients. This state is called “waiting” and is the browser’s way of ensuring that only one version of the Service Worker is running at a time. The above example is because the V2 Worker has not been activated. In the DevTools “Application” TAB, you’ll see the new Service Worker waiting:
Even if you open only one TAB in the demo, the new version does not appear when the page is refreshed. The reason is how browser navigation works. When navigating, the current page does not disappear until a response header is received, even if the response has a Content-Disposition header. Because of this overlap, the current Service Worker always controls a page when refreshing.
To get updates, you need to close or exit all labels that use the current Service Worker. Then look at the example and you’ll see a picture of the horse.
Activate
When the old Service Worker exits, Activate is triggered, and the new Service Worker can control the client. At this point you can take care of migrating the database or clearing the cache. In the above demonstration, all other casses are removed in the Activate event, thus removing the old static-v1 cache as well.
Note: Do not update previous versions. It could be many older versions of Service workers
Pass a promise to event.waitUntil(), which buffers functional events (fetch, push, sync, etc.) until the promise is resolved. Therefore, when the FETCH event is triggered, the activation is complete.
Skip the wait phase
The new Service Worker can be activated as soon as possible by calling self.skipwaiting ().
This causes the Service Worker to eject the currently active Worker and activate itself as soon as it enters the wait phase (or immediately, if it is already in the wait phase). This does not allow the Worker to skip the installation, just the wait phase.
It makes no difference whether skipWaiting() is called during a wait or before. It is typically called in the install event:
self.addEventListener('install', event => {
self.skipWaiting();
event.waitUntil(
// caching etc
);
});
Copy the code
Avoid changing the url of the Service Worker script
Do not arbitrarily change the address of the Service Worker. This can cause the following problems:
- Index.html registers sw-v1.js as a Service Worker.
- Sw-v1.js caches and provides index.html for offline priority.
- Update index.html to register the brand new SW-v2.js.
By doing this, the user will never be able to get sw-v2.js because sw-v1.js will supply the old version of index.html from its cache.
Note: The examples above are for demonstration purposes only
Development and debugging
Update on reload
This can make the lifecycle developer friendly. Each time you browse, you will:
- Re-extract the Service Worker.
- Install it as a new version even if the bytes are identical, which means running the Install event and updating the cache.
- Skip the wait phase to activate the new Service Worker.
- Browse the page. This means that updates are made every time you browse, including refresh, without having to reload twice or close the TAB.
Skip waiting
Listen for updates through the API
The Service Worker exposes the API to listen for update status changes, with the following code:
navigator.serviceWorker.register('/sw.js').then(reg => {
reg.installing; // the installing worker, or undefined
reg.waiting; // the waiting worker, or undefined
reg.active; // the active worker, or undefined
reg.addEventListener('updatefound', () => {// When a service worker installs const newWorker = reg.installing; newWorker.state; //"installing"The installation event has been triggered but has not yet completed //"installed"Installation complete //"activating"The activation event has been triggered but has not yet completed //"activated"Activation complete //"redundant"Discarded. Either the installation failed or was replaced by a new version. newWorker.addEventListener('statechange', () => {// newworker.state}); }); }); navigator.serviceWorker.addEventListener('controllerchange', () => {// when the service worker controls the page});Copy the code