The Web for all. The power of Progressive Web Apps.

Staff Developer Advocate at 

Patricio Vargas (Pato)

  • Google Developer Expert on Web Technologies
  • Microsoft MVP
  • Auth0 Ambassador
  • AWS Community Builder
  • Postman Supernova
  • Twilio Champion
  • Media Developer Expert at Cloudinary
  • Women Techmaker

@devpato

What is a PWA?

@devpato

What is a PWA?

A PWA stands for Progressive Web Applications. PWAs are web apps that have the ability to be installed on your phone as a native app.

 

@devpato

Who uses PWA?

@devpato

What are the superpowers of a PWA?

PWA Superpowers

  • Instalability
  • SEO Friendly
  • Push Notifications
  • Performance
  • User Experience
  • Offline Experience
  • Payments: PayPal, Apple Pay, Google Pay, etc
  • Multiplatform

@devpato

What are the disadvantages of PWA

Disadvantages of PWA

  • Caching Can Be Dangerous
  • Less Access To System Features
  • No Review Standard
  • Apple Needs to catchup with the technology

@devpato

Why did I fall in love with PWA?

 

My Story with PWA

  • PWA is in more than 120 countries
  • 1/5 Americans have products of our client
  • Reduced the number of server calls by 32%
  • Increased users by millions
  • Improved UX
  • Products were easier to be found on the web
  • PWA load time was ~3 sec
  • By 2019, the client was earning $8.8B 

@devpato

@devpato

Main Components
Of A PWA

 

 

PWA Components

  • Web Applications
  • Manifest.json
  • Service Worker (sw.js, serviceWorker.js)

@devpato

Web Applications

@devpato

Manifest.json

The manifest.json is a simple JSON file on your website that tells the browser about your website on the user's desktop or mobile device. The browser requires a manifest to display the Add to Home Screen message or icon.

@devpato

Service Worker

A Service worker is a proxy script between your web app and the outside. Service Workers execute separately from the main browser thread.

Web App

Service Worker

Cache

Network

@devpato

What are the superpowers of a
Service Worker?

Service Worker superpowers 

  • Network requests.
  • Handle how network requests are done on your website.
  • Make use of the Background Sync API.
  • Cache things from your website.
  • Receive push notifications when the app is not active.
  • Stay inactive when it's not in use.
  • Used to make your app work offline.
  • Display an offline page in case of connection failure.
  • Capture offline metrics.
  • Perform load balancing in the client side.

@devpato

PRE-CACHE

VS

RUNTIME CACHE

@devpato

Pre-Cache

Your resources are put in the cache before they are requested.

E.g Your web app start URL, offline fallback, and, key js and files

 

Pre-caching happens at the service worker install event at the cache first strategy

 

@devpato

Runtime Cache

Runtime cache adds resources to the cache when they are requested.  Runtime caching works with different caching strategies and the resources are cached independently.

 

E.g a new cache named "images".

 

 

 

@devpato

Service Worker Lifecycles

index.js

sw.js

@devpato

Registering the Service Worker

if ("serviceWorker" in navigator) {
    window.addEventListener("load", ()=>{
        navigator.serviceWorker.register("sw.js").then(swRegistered => {
            console.log("[ServiceWorker**] - Registered");
        });
    });
}

index.js

@devpato

Installing the Service Worker

const cacheName = "my-pwa-shell-v1.0";
const filesToCache = [
    "index.html",
    "./js/index.js",
    "./styles/styles.css",
    "manifest.json",
    "./assets/icons/icon.png",
    "./assets/nike1.jpeg",
];


self.addEventListener("install", e => {
    console.log("[ServiceWorker] - Install");
    e.waitUntil((async () => {
		const cache = await caches.open(cacheName);
		console.log("[ServiceWorker] - Caching app shell");
		await cache.addAll(filesToCache);
    })());
});

sw.js

@devpato

Activating Service Worker

self.addEventListener("activate", e => {
    e.waitUntil((async () => {
        const cacheList = await caches.keys();
        await Promise.all(
            cacheList.map(currentCache => {
            	// currentCache = "cacheVersion-1.0" , newCache = "cacheVersion-2.0"
                if (currentCache !== newCache) { 
                    return caches.delete(currentCache);
                }
            })
        );
    })());
});

sw.js

@devpato

Fetching
(Intercepting Network Call)

self.addEventListener('fetch', e => {
    e.respondWith((async () => {
        const resource = await caches.match(e.request);
        
        return resource || fetch(e.request);
    })());
});

sw.js

@devpato

Workbox

@devpato

What is Workbox?

Workbox is a library that incorporates a set of best practices and eliminates the boilerplate that every developer writes when working with service workers.” - Google Devs.

  • Precaching
  • Runtime caching
  • Caching Strategies
  • Request Routing
  • Background Sync
  • Helpful Debugging

@devpato

Caching Strategies

@devpato

2

fallback

Web App

Cache

Network

Service Worker

1

4

Stale-While Revalidate

3

@devpato

self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.open('mysite-dynamic').then(function (cache) {
      return cache.match(event.request).then(function (response) {
        var fetchPromise = fetch(event.request).then(function (networkResponse) {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        });
        return response || fetchPromise;
      });
    }),
  );
});
import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';

registerRoute(
  ({url}) => url.pathname.startsWith('/images/avatars/'),
  new StaleWhileRevalidate()
);

With Workbox.js

Without Workbox.js

Stale-While Revalidate

@devpato

Cache, falling back to the Network

2

fallback

Web App

Network

Service Worker

1

The Asset

3

@devpato

self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.match(event.request).then(function (response) {
      return response || fetch(event.request);
    }),
  );
});
import {registerRoute} from 'workbox-routing';
import {CacheFirst} from 'workbox-strategies';

registerRoute(({request}) => request.destination === 'style', new CacheFirst());

With Workbox.js

Without Workbox.js

Cache, falling back to the network

@devpato

Network, falling back to the cache

3

fallback

2

Web App

Cache

Network

Service Worker

1

The Asset

@devpato

self.addEventListener('fetch', function (event) {
  event.respondWith(
    fetch(event.request).catch(function () {
      return caches.match(event.request);
    }),
  );
});
import {registerRoute} from 'workbox-routing';
import {NetworkFirst} from 'workbox-strategies';

registerRoute(
  ({url}) => url.pathname.startsWith('/social-timeline/'),
  new NetworkFirst()
);

With Workbox.js

Without Workbox.js

Network, falling back to the cache

@devpato

Network Only

2

Web App

Network

Service Worker

1

The Asset

@devpato

self.addEventListener('fetch', function (event) {
  event.respondWith(fetch(event.request));
  // or simply don't call event.respondWith, which
  // will result in default browser behavior
});
import {registerRoute} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';

registerRoute(({url}) => url.pathname.startsWith('/admin/'), new NetworkOnly());

With Workbox.js

Without Workbox.js

Network Only

@devpato

Cache Only

2

Web App

Cache

Service Worker

1

The Asset

@devpato

self.addEventListener('fetch', function (event) {
  // If a match isn't found in the cache, the response
  // will look like a connection error
  event.respondWith(caches.match(event.request));
});
import {registerRoute} from 'workbox-routing';
import {CacheOnly} from 'workbox-strategies';

registerRoute(({url}) => url.pathname.startsWith('/app/v2/'), new CacheOnly());

With Workbox.js

Without Workbox.js

Cache Only

@devpato

DEMO

@devpato

Resources

@devpato

React PWA - AOT

By Patricio Vargas

React PWA - AOT

  • 241