Using Service Worker and PWA in your project

Booklet

This is the study material I compiled. It is very systematic and complete. You are welcome to study together.

  • Modern JavaScript Advanced Booklet

  • Introduction to Dart in simple terms

  • Modern TypeScript Advanced Booklet

  • linwu’s algorithm notes

Introduction

Recently, the next project has used pwa technology, which is not complicated to use. The current browser compatibility is also relatively good.

A service worker is a script in the browser that runs independently of a web page, while a PWA (Progressive Web App) is a web application that looks and feels like a native application. Before discussing Service Workers vs. PWA, let’s take a brief look at Web Workers.

Web Worker

1. What is a Web Worker?

Web Worker is a thread built into the browser that executes JavaScript code in a non-blocking event loop. Since JavaScript is a single-threaded language, it can only handle one task at a time. The emergence of complex tasks may cause the main thread to be blocked, seriously affecting the user experience. The purpose of a Web Worker is to allow the main thread to create worker threads so that they can run simultaneously. The Worker thread is mainly responsible for processing complex calculation tasks and then returning the results to the main thread. In short, the worker thread performs complex calculations while keeping the page (main thread) fluid without blocking.

2. Type

There are three main types of Web Workers:

  1. Dedicated Workers are instantiated by the main thread and can only communicate with the main thread.

  2. Shared Workers [Shared Workers] can be accessed by all threads from the same source.

  3. Service Workers [Service Workers] can control their associated web pages, intercept and modify navigation, resource requests, and cache resources, allowing you to flexibly control the behavior of the application under certain circumstances.

3. Restrictions

1. Same origin restriction

The script file assigned to the Worker thread must have the same origin as the script file of the main thread, and usually should be placed under the same project.

2. DOM restrictions

Web Workers do not have access to certain key JavaScript features, including:

1 DOM (as this may lead to thread unsafety)

2 window objects

3 document objects

4 parent object

3. File restrictions

For security reasons, worker threads cannot read local files. The scripts they load must come from the network and must have the same origin as the main thread’s scripts.

What is a Service Worker?

A Service Worker is a script that runs behind the browser to provide powerful offline and caching capabilities to improve the performance and reliability of web applications. It is a key component of Progressive Web Apps (PWA), which can make web applications more like native applications and work even when offline. Service Worker is a powerful tool in web development that allows developers to better control and manage resource caching, network requests, and responses for web pages, thereby providing a faster and more stable user experience.

image.png

Features and advantages of Service Worker

Service Workers provide many important features and benefits, including:

1. Offline support

A service worker can cache a web application’s resources so that it can still load and run even when the network is disconnected or has low network quality. This means users can access the application at any time without relying on a network connection.

2. Faster loading speed

By caching resources locally, Service Workers can significantly improve the loading speed of web pages. It can fetch resources from the cache without re-downloading them from the server every time.

3. Support background synchronization

Service Workers allow tasks to be performed in the background, such as data synchronization or push notifications. This allows the application to perform some important operations without disturbing the user.

4. Enhanced security

Service Workers are subject to the same-origin policy, so it provides more secure resource caching and request handling. It can also be used to intercept and handle malicious requests.

5. Support push notification

Service Worker has a push notification function that can send real-time notifications to users through the browser to improve user engagement and retention.

Use

// Register Service Worker
if ('serviceWorker' in navigator) {<!-- -->
  navigator.serviceWorker.register('/sw.js')
    .then(function(registration) {<!-- -->
      console.log('Service Worker registration successful:', registration);
    })
    .catch(function(error) {<!-- -->
      console.log('Service Worker registration failed:', error);
    });
}

//Cache resources in Service Worker
self.addEventListener('install', function(event) {<!-- -->
  event.waitUntil(
    caches.open('my-cache').then(function(cache) {<!-- -->
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js'
      ]);
    })
  );
});

//Intercept network requests and return resources from cache
self.addEventListener('fetch', function(event) {<!-- -->
  event.respondWith(
    caches.match(event.request).then(function(response) {<!-- -->
      return response || fetch(event.request);
    })
  );
});

We first try to register a Service Worker in the browser and specify the resources to be cached. Then, in the Service Worker, we cache these resources by listening to the install event, and intercept network requests in the fetch event to return the resources from the cache. This way, even when offline, the page will still be able to load the required resources.

Life cycle

The service worker’s life cycle is completely separated from the web page. It includes the following stages:

image.png

  • download
  • Install
  • activation

1.Download

The first time a user visits a website or page controlled by a service worker, the service worker will be downloaded immediately. The browser downloads the .js file containing the Service Worker.

2.Installation

You need to register on the web page to install. Before installation, you need to check whether serviceWorker is supported. If it is supported, register() will be called every time the page is loaded, and the browser will determine whether it has been registered. An important detail of the register() method is the location of the Service Worker file. In this example, you can see that the Service Worker file is located at the root of the domain, which means that the Service Worker scope will be under this domain. In other words, this Service Worker will receive fetch events for everything in this domain. If we register the Service Worker file in /example/sw/sw.js, then the Service Worker will only see the fetch events for pages starting with /example/ (for example, /example/page1/, /example/page2/).

if ('serviceWorker' in navigator) {<!-- -->
  window.addEventListener('load', function() {<!-- -->
    navigator.serviceWorker.register('/sw/sw.js').then(function(registration) {<!-- -->
      // registration success
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {<!-- -->
      // registration failed
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

After successful registration, the install event will be triggered, caches.open() will be called with the cache name we want, and then cache.addAll() will be called and the file array will be passed in. This is a chain of promises ( caches.open() and cache.addAll() ). The event.waitUntil() method accepts a promise and uses it to know how long the installation will take and whether it was successful. If all files are successfully cached, the service worker will be installed. If one of the files fails to download, the installation step will fail. If the cache file list is too long, it will increase the chance of failure.

var CACHE_NAME = 'my-cache';
var urlsToCache = [
  '/',
  '/styles/main.css',
  '/script/main.js'
];

self.addEventListener('install', function(event) {<!-- -->
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {<!-- -->
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

3.Activate

The next step is to enter the activation state: Activate. The Service Worker can be updated in this state.

  1. When the user navigates to the site, the browser attempts to re-download the script file that defines the Service Worker in the background. If a Service Worker file is byte different from the file it is currently using, it is considered a new Service Worker.
  2. The new Service Worker will be started and the install event will be fired.
  3. The old Service Worker still controls the current page, so the new Service Worker will enter the waiting state.
  4. When the currently open page on the website is closed, the old Service Worker will be terminated and the new Service Worker will take control.
  5. After the new Service Worker takes control, its activate event will be triggered.
self.addEventListener('activate', function(event) {<!-- -->
var cacheAllowlist = ['pages-cache-v1', 'blog-posts-cache-v1'];
event.waitUntil(
    caches.keys().then(function(cacheNames) {<!-- -->
      return Promise.all(
        cacheNames.map(function(cacheName) {<!-- -->
          if (cacheAllowlist.indexOf(cacheName) === -1) {<!-- -->
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

Caching and request response optimization

Strategy

  • cache first
  • network first
  • Only use cache
  • Use network only
  • Speed priority

Once the Service Worker is installed and the user navigates to a different page or refreshes the current page, the Service Worker will start listening to the fetch event.

The workflow of the cache priority strategy: First, it listens to the browser’s fetch event and intercepts the original request. Next, it checks whether the resource to be requested exists in the cache, and if so, returns the resource in the cache directly. It then initiates a remote request to obtain the latest resource, caches the resource, and returns it to the page.

self.addEventListener('fetch', function(event) {<!-- -->
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {<!-- -->
        if (response) {<!-- -->
          return response;
        }
        var fetchRequest = event.request.clone();

        return fetch(fetchRequest).then(
          function(response) {<!-- -->
            if(!response || response.status !== 200 || response.type !== 'basic') {<!-- -->
              return response;
            }

            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {<!-- -->
                cache.put(event.request, responseToCache);
              });

            return response;
          }
        );
      })
    );
});

What is a PWA?

PWA is a method of creating cross-platform web applications using modern web APIs and traditional progressive enhancement strategies. It combines the discoverability, ease of installation, and linkability of web applications with the performance and interactivity of native applications.

Advantages

Progressiveness

PWA works across all browsers because it is developed with progressive enhancement in mind, so users don’t need to worry about browser compatibility issues.

Connection independence

PWA can be accessed normally when offline or with poor network conditions, relying on Service Worker technology, which makes the user experience more stable.

Native-like application

Because it is developed on the basis of the App Shell model, PWA has a user interaction experience similar to native applications, providing users with higher satisfaction.

Continuous updates

PWAs are always up-to-date without the need for manual updates by users, which eliminates the hassle of version management.

Security

Served via HTTPS protocol, protecting user data from prying eyes and ensuring content is not tampered with.

Indexable

PWA manifest files and service workers can be indexed by search engines, improving the visibility of the application.

Sticky

Through functions such as push offline notifications, PWA can attract users to return and increase user engagement.

Installable

Users can add commonly used Web Apps to the desktop without going to the app store to download and install them, which improves usability.

Linkable

Content can be shared through simple links without downloading and installation, making it convenient and practical.

Disadvantages

Low access to system features

Compared with native apps, PWAs have relatively low access to device system functions, and some advanced features may be restricted.

There is no unified review standard

Unlike app stores, there are no unified review standards for PWAs, which may result in some apps of varying quality entering the market.

Core Technology

3. Core technology

  • Web App Manifest Web App Manifest (Web application manifest) is a file that centrally writes page-related information and configuration in JSON form.
{<!-- -->
  "name": "My PWA",
  "short_name": "PWA",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#007bff",
  "icons": [
    {<!-- -->
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {<!-- -->
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

  • start_url can set the startup URL
  • icons will help me set the icons for the page at each resolution.
  • background_color sets the background color that Chrome uses as soon as the web app is launched and remains on the screen until the web app is first rendered.
  • theme_color will set the theme color
  • display sets startup style
  • Service Worker
  • Notifications API Notifications API
  • Push API Push API Push API can be used to push new content from the server without client intervention. It is implemented by the application’s Service Worker; the notification function can display some new information to the user through the Service Worker, or at least Remind users that the app has been updated with certain features.