10. Web Storage and PWAs

Web Storage and IndexedDB

Local and Session Storage

Cookies; ~4 kB max, sent to server on every request. HTML5 storage supports ~5 MB max.

Storage is key-value string pairs accessible to JS on a specific origin (protocol + domain + port).

Two types of storage:

IndexedDB

A indexed NoSQL DB:

CacheStorage

Accessible to service workers and hence requires HTTPS.

Stores pairs of request and response objects; for caching web resources.

Can be few hundred MBs.

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open("my-cache").then(async cache => {
      // Fetch, then add to cache
      await cache.addAll(
        [
          '/css/bootstrap.css',
          '/css/main.css',
          '/js/bootstrap.min.js',
          '/js/jquery.min.js',
          '/offline.html'
        ]
      );

      // Shorthand for:
      await cache.add(new Request("some-path"));

      // Can also put arbitrary data into cache
      await cache.add("some-path", new Response("some-data"))
    })
  );
}); 

self.addEventListener('fetch', event => event.respondWith(
    caches.open("my-cache")
    .then(cache => cache.match(event.request))
    .then(response => {
      if (response == undefined) {
        // Not in cache
        return fetch(event.request).then(response => {
          // Response/request body can only be read once, so use `clone`
          cache.put(event.request, response.clone());
          return response;
        });
      }
      return response;
    })
  )
);

Progressive Web Apps

Appear to be ‘installed’ like native apps: begin life in a browser tab and can be ‘installed’.

A PWA must:

It should:

Service Workers

Proxy ‘servers’ between the web app and network. Runs headless in its own thread, and must use HTTPS.

Service workers are used for notifications and background sync.

Service workers must be started by the web page:

if ("serviceWorker" in navigator) window.addEventListener("load", () => {
  navigator.serviceWorker.register("./worker.js").then(registration => {
    console.log(`Scope: ${registration.scope}`);
  }).catch(err => console.error(err));
});

After installation, it can be in a few states:

Using service workers and CacheStorage:

const cacheName = "name";
const precacheResources = ["/", "/index.html", "/main.css"];

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

self.addEventListener("activate", () => console.log("Activated!"));

self.addEventListener("fetch", event => {
  console.log(`Intercepting fetch to ${event.request.url}`);
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
      if (cachedResponse) return cachedResponse;
      // Fallback to making a network request if not in cache
      return fetch(event.request);
    })
  )
});

WebAssembly

Binary code that is pre-compiled to wasm bytecode - supported languages include C, C++ and Rust.

Ahead-of-time or just-in-time (interpreted) compilation of wasm.