@lyzadanger | www.lyza.com
Smashing Freiburg 2016
https://github.com/lyzadanger/pragmatist-service-worker
Text
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
</script>index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Service Workers: Offline Message</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/default.css" rel="stylesheet" title="Default Style">
</head>
<body>
<p>If you see this, you are online</p>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
</script>
</body>
</html>self.addEventListener(
);service-worker.js
self.addEventListener('fetch',
);self.addEventListener('fetch', event => {
});self.addEventListener('fetch', event => {
});service-worker.js
if (event.request.mode === 'navigate') {
} event.respondWith(/* ... */);self.addEventListener('fetch', event => {
});service-worker.js
if (event.request.mode === 'navigate') {
} event.respondWith( )); event.respondWith(fetch(event.request));self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(fetch(event.request));
}
});service-worker.js
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(fetch(event.request));
}
});service-worker.js
event.respondWith(fetch(event.request));
fetch(event.request)
fetch(event.request)
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
}
});self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(
);
}
});self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request)
);
}
});self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request).catch(error => {
})
);
}
});self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request).catch(error => {
return new Response();
})
);
}
});self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request).catch(error => {
return new Response('<p>Oh, dear.</p>',
{ headers: { 'Content-Type': 'text/html' } });
})
);
}
});
if (event.request.mode === 'navigate')service-worker.js
if (event.request.mode === 'navigate' ||
(event.request.method === 'GET' &&
if (event.request.mode === 'navigate' ||
(event.request.method === 'GET' &&
event.request.headers.get('accept').includes('text/html'))) event.respondWith(
/* Promise resolving to a Response, ideally */
);
event.respondWith(
fetch(request)
);
event.respondWith(
fetch(request).catch(error => { })
);
event.respondWith(
fetch(request).catch(error => {
return new Response(...);
})
);
event.respondWith(
fetch(request).catch(error => {
return new Response('<p>Oh, dear.</p>');
})
);
event.respondWith(
fetch(request).catch(error => {
return new Response('<p>Oh, dear.</p>',
{
headers: { 'Content-Type': 'text/html' }
}
);
})
);
service-worker.js
self.addEventListener('install', event => {
});
var offlineURL = 'offline.html';
event.waitUntil(
/* Gotta stick offline page in cache */
);
self.addEventListener('install', event => {
var offlineURL = 'offline.html';
event.waitUntil(
fetch(...).then(...)
);
});
service-worker.js
self.addEventListener('install', event => {
var offlineURL = 'offline.html';
event.waitUntil(
fetch(...).then(...)
);
});
self.addEventListener('install', event => {
var offlineURL = 'offline.html';
event.waitUntil(
fetch(new Request(offlineURL)).then(...)
);
});
self.addEventListener('install', event => {
var offlineURL = 'offline.html';
event.waitUntil(
fetch(new Request(offlineURL)).then(response => {
})
);
});
self.addEventListener('install', event => {
var offlineURL = 'offline.html';
event.waitUntil(
fetch(new Request(offlineURL)).then(response => {
})
);
});
service-worker.js
return caches.open('offline').then(cache => {
});self.addEventListener('install', event => {
var offlineURL = 'offline.html';
event.waitUntil(
fetch(new Request(offlineURL)).then(response => {
return caches.open('offline').then(cache => {
});
})
);
});
service-worker.js
return cache.put(offlineURL, response);
self.addEventListener('fetch', event => {
var request = event.request;
if (request.mode === 'navigate' ||
(request.method === 'GET' &&
request.headers.get('accept').includes('text/html'))) {
event.respondWith(
);
}
});service-worker.js
self.addEventListener('fetch', event => {
var request = event.request;
if (request.mode === 'navigate' ||
(request.method === 'GET' &&
request.headers.get('accept').includes('text/html'))) {
event.respondWith(
fetch(request)
})
);
}
});self.addEventListener('fetch', event => {
var request = event.request;
if (request.mode === 'navigate' ||
(request.method === 'GET' &&
request.headers.get('accept').includes('text/html'))) {
event.respondWith(
fetch(request).catch(error => {
})
);
}
});self.addEventListener('fetch', event => {
var request = event.request;
if (request.mode === 'navigate' ||
(request.method === 'GET' &&
request.headers.get('accept').includes('text/html'))) {
event.respondWith(
fetch(request).catch(error => {
return caches.open('offline')
})
);
}
});self.addEventListener('fetch', event => {
var request = event.request;
if (request.mode === 'navigate' ||
(request.method === 'GET' &&
request.headers.get('accept').includes('text/html'))) {
event.respondWith(
fetch(request).catch(error => {
return caches.open('offline').then(cache => {
});
})
);
}
});self.addEventListener('fetch', event => {
var request = event.request;
if (request.mode === 'navigate' ||
(request.method === 'GET' &&
request.headers.get('accept').includes('text/html'))) {
event.respondWith(
fetch(request).catch(error => {
return caches.open('offline').then(cache => {
return cache.match('offline.html');
});
})
);
}
});event.respondWith(
fetch(request).catch(error => {
})
);
service-worker.js
return caches.open('offline').then(cache => {
});
return caches.open('offline').then(cache => {
return cache.match('offline.html');
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
index.html
navigator
register
feature testing
service worker file location
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js'
);
}index.html
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js',
{ scope : './' }
);
}self.addEventListener('fetch', event => {
var request = event.request;
});service-worker.js
if (isNavigateRequest(request)) {
} // Handle request for HTML: network-first } else if (isImageRequest(request)) {
// Handle request for images: cache-first
}self.addEventListener('fetch', event => {
var request = event.request;
if (isNavigateRequest(request)) {
// Handle request for HTML: network-first
} else if (isImageRequest(request)) {
// Handle request for images: cache-first
}
});function isNavigateRequest (request) {
return (request.mode === 'navigate' ||
(request.method === 'GET' &&
request.headers.get('accept').includes('text/html')));
}function isImageRequest (request) {
return (request.headers.get('Accept').indexOf('image') !== -1);
}if (isNavigateRequest(request)) {
}service-worker.js
event.respondWith(
fetch(request)
);.then(response => addToCache(request, response))function addToCache (request, response) {
}service-worker.js
// You do not want to cache a bad response!
if (response.ok) {
}
// For Promise chaining
return response;
// A Response can only be "used" once
const copy = response.clone(); caches.open('assets').then(cache => {
cache.put(request, copy);
});
if (isNavigateRequest(request)) {
}service-worker.js
event.respondWith(
fetch(request)
);.then(response => addToCache(request, response)).catch(() => fetchFromCache(request))function fetchFromCache (request) {
}service-worker.js
return caches.match(request).then(response => {
});
function fetchFromCache (request) {
}service-worker.js
return caches.match(request)
);
if (!response) {
throw Error(`${request.url} not found in cache`);
}
return response;
return caches.match(request).then(response => {
});
if (isNavigateRequest(request)) {
event.respondWith(
fetch(request)
.then(response => addToCache(request, response))
.catch(() => fetchFromCache(request))
.catch(() => offlinePage())
);
}service-worker.js
function offlinePage () {
return caches.open('offline').then(cache => {
return cache.match('offline.html');
});
}else if (isImageRequest(request)) {
event.respondWith(
);
}service-worker.js
fetchFromCache(request) // Cache first
.catch(() => fetch(request) // But if not in cache, fetch
// If fetch was successful, add to cache for later
.then(response => addToCache(request, response)))
// If fetch failed...
.catch(() => console.log('unable to respond to request'))service-worker.js
const cacheFiles = [
'',
'default.css',
'static-assets/cloud-1.jpg',
'static-assets/cloud-2.jpg',
'static-assets/cloud-3.jpg',
'static-assets/cloud-4.jpg',
'static-assets/cloud-5.jpg',
'static-assets/cloud-6.jpg',
'static-assets/cloud-7.jpg',
'static-assets/cloud-8.jpg',
'static-assets/cloud-9.jpg',
'static-assets/cloud-10.jpg'
];service-worker.js
self.addEventListener('install', event => {
event.waitUntil(
);
});self.addEventListener('install', event => {
event.waitUntil(
caches.open('shell').then(cache => {
})
);
});service-worker.js
self.addEventListener('install', event => {
event.waitUntil(
caches.open('shell').then(cache => {
})
);
});self.addEventListener('install', event => {
event.waitUntil(
caches.open('shell').then(cache => {
return cache.addAll(cacheFiles);
})
);
});self.addEventListener('install', event => {
event.waitUntil(
caches.open('shell').then(cache => {
return cache.addAll(cacheFiles);
}).then(() => self.skipWaiting())
);
});index.html
self.addEventListener('fetch', event => {
var request = event.request;
});self.addEventListener('fetch', event => {
var request = event.request;
var url = new URL(request.url);
});self.addEventListener('fetch', event => {
var request = event.request;
var url = new URL(request.url);
if (cacheFiles.indexOf(url.pathname) !== -1) {
}
});self.addEventListener('fetch', event => {
var request = event.request;
var url = new URL(request.url);
if (cacheFiles.indexOf(url.pathname) !== -1) {
event.respondWith(
fetchFromCache(request)
);
}
});self.addEventListener('fetch', event => {
var request = event.request;
var url = new URL(request.url);
if (cacheFiles.indexOf(url.pathname) !== -1) {
event.respondWith(
fetchFromCache(request)
.catch(() => fetch(request))
);
}
});const cachePrefix = 'mission-05';service-worker.js
self.addEventListener('activate', event => {
event.waitUntil(
);
});service-worker.js
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheKeys => {
})
);
});service-worker.js
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheKeys => {
})
);
}); var oldCacheKeys = cacheKeys.filter(key => {
return (key.indexOf(cachePrefix) !== 0);
}); var deletePromises = oldCacheKeys.map(oldKey => {
return caches.delete(oldKey);
});return Promise.all(deletePromises);service-worker.js
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheKeys => {
var oldCacheKeys = cacheKeys.filter(key => {
return (key.indexOf(cachePrefix) !== 0);
});
var deletePromises = oldCacheKeys.map(oldKey => {
return caches.delete(oldKey);
});
return Promise.all(deletePromises);
})
);
});self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheKeys => {
var oldCacheKeys = cacheKeys.filter(key => {
return (key.indexOf(cachePrefix) !== 0);
});
var deletePromises = oldCacheKeys.map(oldKey => {
return caches.delete(oldKey);
});
return Promise.all(deletePromises);
}).then(() => self.clients.claim())
);
});@lyzadanger | lyza.com