Patricio Vargas
Software Engineer
Staff Developer Advocate at
Patricio Vargas (Pato)
@devpato
@devpato
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
@devpato
@devpato
@devpato
@devpato
@devpato
@devpato
@devpato
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
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
@devpato
@devpato
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 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
index.js
sw.js
@devpato
if ("serviceWorker" in navigator) {
window.addEventListener("load", ()=>{
navigator.serviceWorker.register("sw.js").then(swRegistered => {
console.log("[ServiceWorker**] - Registered");
});
});
}
index.js
@devpato
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
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
self.addEventListener('fetch', e => {
e.respondWith((async () => {
const resource = await caches.match(e.request);
return resource || fetch(e.request);
})());
});
sw.js
@devpato
@devpato
“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.
@devpato
@devpato
2
fallback
Web App
Cache
Network
Service Worker
1
4
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
@devpato
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
@devpato
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
@devpato
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
@devpato
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
@devpato
@devpato
@devpato
By Patricio Vargas