Service workers

Service Workers are virtual agents between the browser and the network. They finally solved problems that front-end developers have been grappling with for years – most notably how to properly cache web site resources and make them available when a user’s device is offline.

They run on a thread separate from our page’s JavaScript main thread and do not have any access to the DOM structure. This introduces a different approach to traditional Web programming – the API is non-blocking and can send and receive information between different contexts. You can assign tasks to the Service Worker and, using a promise-based approach, receive the results when the task completes.

They provide more than just offline functionality, including handling notifications, performing heavy computations on separate threads, and more. Service Workers are powerful because they can control network requests, modify network requests, return cached custom responses, or synthesize responses.

Because they are so powerful, Service Workers can only be executed in a secure context (that is, HTTPS). If you want to experiment before pushing your code into production, you can always test on the localhost or set up the GitHub page – both with HTTPS support.

advantage

Offline priority

“Offline first” or “cache first” modes are the most popular strategies for delivering content to users. Offline priority allows users to use the application without losing data even when the network is down. Offline first means always get the best experience based on the current network.

Common caching patterns

Let’s look at several common caching patterns.

Only the cache

This pattern is useful for static resources, as follows:

self.addEventLister("fetch".function(event) { event.respondWith(caches.match(event.request);) ; });Copy the code
Cache is preferred and network is used as the rollback solution

This pattern also requests from the cache accordingly. However, if the content is not found in the cache, the service worker requests it from the network and returns:

self.addEventListener("fetch".function(event) {
    event.respondWith(caches.match(event.resquest).then((response) = >{
        returnresponse || fetch(event.request); }})));Copy the code
Only the network

In the classic Web model, an attempt is made to request data from the network. If the network fails, the request fails.

self.addEventListener("fetch".function(event) {
    event.respondWith(fetch(event.request));
});
Copy the code
Network preferentially, cache is used as rollback solution

Always make a request to the network and return the cached version if the request fails.

self.addEventListener("fetch".function(event) {
    event.respondWith(caches.match(event.resquest).
    catch(() = > {returncaches.match(event.request); }})));Copy the code
Network priority, cache as the rollback solution, general rollback as the bottom-of-the-pocket solution
self.addEventListener("fetch".function(event) {
    event.respondWith(caches.match(event.resquest).
    catch(() = > {return caches.match(event.request).then((response) = >{
            return response || caches.match("/generic.png");
        });
    }));
});
Copy the code
On-demand cache

For resources that do not change very often and resources that do not want to be cached during service worker Install events, you can extend the cache-first, network-as-fallback mode. Saves the requests returned by the network in the cache.

self.addEventListener("fetch".function(event) {
    event.respondWith(caches.open('cache-name').then((cache) = >{
        return caches.match(event.request).then((cachedResponse) = >{
            return cachedResponse || fetch(event.request).then((networkResponse) {
                cache.put(event.request, networkResponse.clone());
                returnnetworkResponse; })}); })); });Copy the code

A clone method is used on the response when it is saved to the cache.

fetch(event.request).then((networkResponse) {
    cache.put(event.request, networkResponse.clone());
    returnnetworkResponse; });Copy the code

This is because you intend to use it more than once (putting it in the cache and using it to correspond to events). Make sure to use the clone command to copy it

The cache is preferred. The network is used as the rollback solution and the cache is updated frequently

For frequently modified resources (such as user profile pictures), you can change the rollback mode of cache preference and network. Even if found in the cache, resources are always requested from the cache.

self.addEventListener("fetch".function(event) {
    event.respondWith(caches.open('cache-name').then((cache) = >{
        return caches.match(event.resquest).then((cachedResponse) = >{
            const fetchPromise = fetch(event.request).then((networkResponse) {
                cache.put(event.request, networkResponse.clone());
                return networkResponse;
            });
            return cachedResponse || fetchPromise;
        });
    }));
});
Copy the code
The network is preferred. The cache is used as the rollback solution and the cache is updated frequently

This pattern always tries to get the latest version from the network and only falls back to the cached version if the network request fails. Each time a network access is successful, the current cache is updated to be treated as a network response.

self.addEventListener("fetch".function(event) {
    event.respondWith(caches.open('cache-name').then((cache) = >{
        return caches.match(event.resquest).then((cachedResponse) = >{
            cache.put(event.request, networkResponse.clone());
            return networkResponse;
        }).
        catch(() = > {return cache.match(event.request);
        });
    }));
});
Copy the code

Summarize the cache strategy

1. Use cache first, network as the rollback solution, and frequently update the cache mode to return the index.html file.

2. Use cache first and network as the rollback solution. Return to all static files that need to be displayed on the home page.

3. Return the Google Map javascript file from the network, or a replacement script if the request fails.

4. Use network first, cache as the rollback solution, and frequently update the cache mode, and return the events.json file.

5. Use on-demand caching mode to return the image file of the event. If the network is not available and the image is not cached, the default universal image is returned.

6. The data analysis request is directly passed without processing.

PWA is progressively enhanced

The purpose is to use the standardized framework provided by the mobile terminal to achieve progressive web applications with user experience similar to Native apps in web applications. Its security, performance, user experience is indeed significantly ahead of other Internet carriers

advantages

reliable

Instant loading, even under uncertain network conditions will not be affected. When a user boots from the home screen, Service Work loads progressive Web applications immediately, completely independent of the network environment. Service Work is like a client proxy that controls the cache and how to respond to resource request logic. By pre-caching critical resources, it eliminates network dependency and ensures an immediate and reliable experience for users.

fast

According to statistics, 53% of users will give up waiting if the site takes longer than 3s to load. After the page is rendered, users expect a smooth experience, transition animations, and fast responses.

Immersive experience

It feels like a native application on the device, with an immersive user experience. Progressive Web applications can be installed and installed on the user’s home screen without having to download and install from the app store. They provide an immersive, full-screen experience and even re-engage users with Web push notifications.

Other features

1. No installation, no download, as long as you enter the URL to visit once, and then add it to the device desktop can continue to use.

  1. Publishing does not need to be submitted to the App Store for approval

  2. Updating iterations does not require review and does not require re-release review

  3. Existing Web pages can be improved into PWA, which can be quickly transformed, put online, realize business and obtain traffic

  4. You don’t need to develop two different versions of Android and IOS

Shortcomings
  1. The technical support of the tour is not complete, not every tour can support all PWA 100%
  2. You need a third-party library to call the underlying hardware (such as the camera)
  3. PWA is not so popular now, some mobile phone manufacturers in China have made manipulation on Android system, it seems to block PWA, but I believe that when PWA is popular, this problem will not be a problem

Service workers in the js13kPWA application

Registration Service Worker

First look at the code to register a new Service Worker in the app.js file:

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/pwa-examples/js13kpwa/sw.js');
};
Copy the code

If the browser supports service worker API, use the ServiceWorkerContainer. The register () method to register on the site. The content is in sw.js file and can be executed after successful registration. It is the only Service Worker code in the app.js file; Everything else about Service workers is in the sw.js file.

After registration, the sw.js file is automatically downloaded, then installed, and finally activated.

The installation

API adds event listeners – the first is the install event:

self.addEventListener('install'.function(e) {
    console.log('[Service Worker] Install');
});
Copy the code

Install’s listener allows you to initialize the cache and add files needed for offline applications.

First, a variable is created as the name of the cache, and the files required by the app shell are logged onto an array

var cacheName = 'js13kPWA-v1';
var appShellFiles = [
'/pwa-examples/js13kpwa/',
'/pwa-examples/js13kpwa/index.html',
'/pwa-examples/js13kpwa/app.js',
'/pwa-examples/js13kpwa/style.css',
'/pwa-examples/js13kpwa/fonts/graduate.eot',
'/pwa-examples/js13kpwa/fonts/graduate.ttf',
'/pwa-examples/js13kpwa/fonts/graduate.woff',
'/pwa-examples/js13kpwa/favicon.ico',
'/pwa-examples/js13kpwa/img/js13kgames.png',
'/pwa-examples/js13kpwa/img/bg.png',
'/pwa-examples/js13kpwa/icons/icon-32.png',
'/pwa-examples/js13kpwa/icons/icon-64.png',
'/pwa-examples/js13kpwa/icons/icon-96.png',
'/pwa-examples/js13kpwa/icons/icon-128.png',
'/pwa-examples/js13kpwa/icons/icon-168.png',
'/pwa-examples/js13kpwa/icons/icon-192.png',
'/pwa-examples/js13kpwa/icons/icon-256.png',
'/pwa-examples/js13kpwa/icons/icon-512.png'
];
Copy the code

Next, the image link parsed from the contents of data/games.js is assigned to another Array, after which the two arrays are merged using array.prototype.concat ().

var gamesImages = [];
for (var i = 0; i < games.length; i++) {
    gamesImages.push('data/img/' + games[i].slug + '.jpg');
}
var contentToCache = appShellFiles.concat(gamesImages);
Copy the code

Then listen for the install event:

self.addEventListener('install'.function(e) {
    console.log('[Service Worker] Install');
    e.waitUntil(caches.open(cacheName).then(function(cache) {
        console.log('[Service Worker] Caching all: app shell and content');
        return cache.addAll(contentToCache);
    }));
});
Copy the code

ExtendableEvent. WaitUntil do? What is caches?

The service worker waits until the code in waitUntil completes before the installation begins. It returns a promise – this process is required because the installation process takes some time and must wait for it to complete.

Caches are a special CacheStorage object that provides the ability to store data within the scope specified by the Service Worker (the second parameter is optional when a Service Worker is registered, Can be used to specify subdirectories of content you want the service worker to control), using web storage in the service worker will not work, Because the execution of a Web storage is synchronous (read: Web Storage does not return a promise), the Cache API is used instead.

Here, a cache is opened with the given name, and all files that the application needs to cache are added to it. When these resources are loaded again, the corresponding cache is available (the requested URL determines whether the cache hit).

The activation

When the Service Worker is installed and activated, the Activate event is triggered. By listening for the Activate event you can do some pre-processing, such as updating old versions, cleaning up unused caches, etc.

Respond to the request

There is also a fetch event that can be used each time an application makes an HTTP request. This event is very useful in that it allows you to intercept requests and respond to them in a custom way. Here is a simple example

self.addEventListener('fetch'.function(e) {
    console.log('[Service Worker] Fetched resource ' + e.request.url);
});
Copy the code

The response to a request can be anything you want: a cached copy of the requested file, or a piece of JavaScript code that does something specific, the possibilities are endless.

When caches exist, they are used to provide services rather than rerequest data. Do this regardless of whether your current application is online or offline. When the requested file is not in the cache, the data is added to the cache before the response.

self.addEventListener('fetch'.function(e) {
    e.respondWith(caches.match(e.request).then(function(r) {
        console.log('[Service Worker] Fetching resource: ' + e.request.url);
        return r || fetch(e.request).then(function(response) {
            return caches.open(cacheName).then(function(cache) {
                console.log('[Service Worker] Caching new resource: ' + e.request.url);
                cache.put(e.request, response.clone());
                return response;
            });
        });
    }));
});
Copy the code

In the above code, the request first looks in the cache to see if the resource is cached. If it is, the cached resource is returned. If it is not, the data is instead requested from the network and cached so that the next time the same request occurs, the cache can be used directly.

The fetchEvent.respondWith method takes over response control and acts as a proxy service between the server and the application. It allows any desired response to each request: the Service Worker takes care of all this, fetching the data from the cache, and modifying it if needed.

As such, the application caches resources when Install fires and returns the cached resources when the FETCH event fires, which is why it can be used even offline. It is also cached whenever new content is added.

Another point to consider is how to update the Service Worker when a new version of the application includes new resources that are available. The version number stored in the cache name is the key to this problem.

How does the Service Worker update?

Service-worker. js controls the caching of page resources and requests. If the JS content is updated, when the browser gets a new file when visiting the website page, it will consider the update and start the update algorithm when the js file is not found byte by byte, so it will install the new file and trigger the install event. However, the old activated Service Worker is still running, and the new Service Worker will enter the waiting state after installation. The new Service Worker does not take effect in the following reopened pages until all opened pages are closed and the old Service Worker stops automatically. What if you want all your pages to be automatically updated in a timely manner when a new version comes out? You can skip the waiting state by executing the self.skipWaiting() method in the install event and go straight to the Activate stage. Then update the Service workers on all clients by executing the self.clients.claim() method when the Activate event occurs.

self.addEventListener('install'.function(event) {
    event.waitUntil(self.skipWaiting());
});

self.addEventListener('activate'.function(event) {
    event.waitUntil(Promise.all([
    // Update the client
    self.clients.claim(),

    // Clean up the old version
    caches.keys().then(function(cacheList) {
        return Promise.all(cacheList.map(function(cacheName) {
            if(cacheName ! = ='cachev1') {
                returncaches.delete(cacheName); }})); }))); });Copy the code

While js files may have browser cache problems, when the file is changed, the browser is still the old file. This causes the update to go unanswered. If you encounter this problem, try adding a filtering rule for the file on the Web Server, not caching it, or setting a short expiration date.

Or manually call update() to update

navigator.serviceWorker.register('/service-worker.js').then(reg = >{
    / / sometime later...
    reg.update();
});
Copy the code

It can be used in conjunction with localStorage instead of loading updates every time

var version = 'v1';
navigator.serviceWorker.register('/service-worker.js').then(function(reg) {
    if (localStorage.getItem('sw_version') !== version) {
        reg.update().then(function() {
            localStorage.setItem('sw_version', version) }); }});Copy the code

Every state has an ing going on.

Cache cleanup

self.addEventListener('activate'.function(e) {
    e.waitUntil(caches.keys().then(function(keyList) {
        return Promise.all(keyList.map(function(key) {
            if (cacheName.indexOf(key) === -1) {
                returncaches.delete(key); }})); })); });Copy the code

Problems with PWA

limitations

1. Low support rate: PWA is not supported on ios and Internet Explorer

2.Chrome has a good share on the desktop in China, but a low share on Android mobile

3. Major vendors have not explicitly supported PWA

4. Dependent GCM service is unavailable in China

5. Competition of wechat mini programs

Reference documentation

First PWA Chinese book github.com/SangKa/PWA-…

[translation] Service Worker portal www.w3ctech.com/topic/866

Micro channel applets and PWA comparative analysis blog.csdn.net/baidu_brows…

The Service Worker best practices x5.tencent.com/tbs/guide/s…