A progressive upgrade for modern web apps
Safe Man in the Middle for network
Treat network as a nice-to-have upgrade
Build for offline first, right next to mobile first
They only work over HTTPS connections
Can only be developed at localhost
Determined by script path (default)
Can be overridden
Caching
Preloading
Web Push Notifications
Background Sync
Periodic Sync
LBS/Geofencing
Chrome, Firefox, Opera & Edge
Safari support coming!
Is ServiceWorker Ready?Reliably register
Install & Activate
Handle network requests, cache data, etc.
Guard with 'serviceWorker' in navigator
Is idempotent
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker
.register('/myServiceWorker.js')
.then(function(reg) {
console.log('Registration successful: ', reg.scope);
}, function(err) {
console.log('Registration failed: ', err);
});
});
}
Resides within service worker script
First opportunity to cache data
Can preload data into cache here
self.addEventListener('install', function(event) {
// Add installation steps/code here
});
Runs after old workers have been stopped
Resides within service worker script
Opportunity to remove old/unused caches and data
self.addEventListener('activate', function(event) {
// Add installation steps/code here
});
Primary event handler in service workers
Opportunity to intercept network requests and cache data
self.addEventListener('fetch', function(event) {
return fetch(event.request); // Don't cache, just pass it through (for now)
});
self.addEventListener('install', function(event) {
event.waitUntil(
caches
.open('siteFiles')
.then(function(cache) {
// Preload the cache with core files for site
return cache.addAll([
'/assets/fonts/font.woff2',
'/assets/fonts/font.otf',
'/assets/fonts/font.ttf',
'/assets/css/main.css',
'/assets/offline.png',
'/offline.html'
]);
});
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches
.match(event.request) // Look for request in ALL caches
.then(function(res) {
if (res) { // We've got it in the cache, return cached copy
return res;
}
if (!navigator.onLine) { // Definite if offline!!
// Return the offline page, already cached in preload during install
return caches.match(new Request('/offline.html'));
}
return fetch(event.request);
});
);
});
During activaton, opportunity to remove unused cached data
self.addEventListener('activate', function(event) {
event.waitUntil(
caches
.keys().then(function(keys) {
return Promise.all(keys.filter(function(key) {
return ['oldDataCache','otherUnusedCache'].indexOf(key) !== -1;
}).map(function(key) {
return caches.delete(key); // Remove old caches of data
}));
});
);
});
During fetch, cache data retrieved from network
const siteFiles = [
'/assets/fonts/font.woff2',
'/assets/fonts/font.otf',
'/assets/fonts/font.ttf',
'/assets/css/main.css',
'/assets/offline.png',
'/offline.html'
];
function fetchAndCache(request) {
return fetch(request)
.then(function(res) {
if (res) {
let cacheName = siteFiles.index(res.url) === -1 ? 'data' : 'siteFiles';
return caches
.open(cacheName)
.then(function(cache) {
return cache.put(request, res.clone());
});
}
});
}
self.addEventListener('fetch', function(event) {
event.respondWith(
caches
.match(event.request) // Look for request in ALL caches
.then(function(res) {
if (res) { // We've got it in the cache, return cached copy
return res;
}
if (!navigator.onLine) { // Definite if offline!!
// Return the offline page, already cached in preload during install
return caches.match(new Request('/offline.html'));
}
return fetchAndCache(event.request); // Fetch and update the cache!
});
);
});
Designed to work hand in hand with service workers
Supports multiple stores of cached of data
Living standard
Is promisy!
Modern replacement for XHR
Simple, easy to use
Living standard
Is promisy!
Parent of ServiceWorkers
Underlying background workers API
Living standard
Generates a service worker for you
Pre-caches site content files
sw-precache --root=dist --config=sw-precache-config.js
// sw-precache-config.js
module.exports = {
navigateFallback: '/index.html',
stripPrefix: 'dist',
root: 'dist/',
staticFileGlobs: [
'dist/index.html',
'dist/**.js',
'dist/**.css',
'dist/fonts/**.woff',
'dist/fonts/**.otf',
'dist/fonts/**.ttf'
]
};
Express-style routes and request handling
Adds policies like middleware
Network first, Cache first, etc.
Caching configuration (expiration, maxEntries, etc.)
Works with sw-precache
importScripts('/sw-toolbox.js');
// Toolbox way of wiring up the offline page:
toolbox.router.get('/*', function(request, values, options) {
return toolbox.networkFirst(request, values, options)
.catch(function(err) {
return caches.match(new Request('/offline.html'));
});
}, {
networkTimeoutSeconds: 5,
cache: {
name: 'staticFiles',
maxAgeSeconds: 60 * 60 * 24 * 30 // one month
}
});
// Retrieve data as quickly as possible:
toolbox.router.get('/api/comments*', function(request, values, options) {
return toolbox.fastest(request, values, options); // Generally use cache, keeps cache fresh
}, {
networkTimeoutSeconds: 5,
cache: {
name: 'comments',
maxAgeSeconds: 60 * 60 * 15 // Keep data for 15 minutes
}
});