SlideShare une entreprise Scribd logo
1  sur  160
ServiceWorkers
@PatrickKettner
@PatrickKettner
@PatrickKettner
@PatrickKettner
ServiceWorkers!
ServiceWorkers!
Push Notifications
Background Sync
Offline Experiences
o
Y
c
oh hey!
You really enjoy looking
close, don’t you?
Offline Experiences
AppCache
CACHE MANIFEST
# v1
index.html
cache.html
cache.html?v2
style.css
image1.png
AppCache
CACHE MANIFEST
# v1
index.html
cache.html
cache.html?v2
style.css
image1.png
AppCache
CACHE MANIFEST
# v1
index.html
cache.html
cache.html?v2
style.css
image1.png
AppCache
CACHE MANIFEST
# v1
index.html
cache.html
cache.html?v2
style.css
image1.png
AppCache
CACHE MANIFEST
# v1
index.html
cache.html
cache.html?v2
style.css
image1.png
AppCache
Static list of files
ALWAYS takes over, even when online
Updates only if the AppCache itself changes
Redirects are treated as failures, and 404
You can cache the AppCache,
causing it to never upgrade
AppCache Sucks
ServiceWorkers Don’
ServiceWorkers Don’
Let’s find out why!
Registration
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
}
// Controls any URL on the same domain
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/js/sw.js")
}
// Controls any URL on the same domain that
// starts with "/js"
// main.js
Except…
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/js/sw.js", {
scope: "/"
})
}
// main.jsService-Worker-Allowed: /js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/js/sw.js")
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/js/sw.js", {
scope: "/"
})
}
// main.js
Service-Worker-Allowed: /js
// Controls any URL on the same domain
Handling Failure
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
.then()
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
.then(function() {// it worked!})
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
.then(function(registration) {// it worked!})
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
.then(function(registration) {// it worked!})
.catch(function(err) {
// it failed :[
})
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
.then(function(registration) {// it worked!})
.catch(function(err) {
console.log("service worker failed:", err)
})
}
// main.js
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
.then(function(registration) {// it worked!})
.catch(function(err) {
navigator.sendBeacon("/err", err)
})
}
// main.js
ServiceWorker
ServiceWorker?
WebWorker
commit 90b52e847359ae902d3f7ce7bc511cadfbc29ea8
Author: Alexey Proskuryakov <ap@webkit.org>
Date: Thu Nov 6 2008 07:04:47 +0000
Implement Worker global object
self.onmessage = (e) {
let result =
doSomething(e.data)
postMessage(result)
}
var myWorker = new Worker('worker.js');
myWorker.postMessage(’testing 123');
myWorker.onmessage = (e) => {
doSomethingWithResult(e.data);
}
WebWorkers
WebWorkers
React to content you send them
ServiceWorkers
React to external (network) events
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
.then(function(registration) {// it worked!})
.catch(function(err) {
navigator.sendBeacon("/err", err)
})
}
// main.js
self.addEventListener('install', (event) => {
// Do some stuff
});
// sw.js
self.addEventListener('install', (event) => {
// Do some stuff
});
// sw.js
self?
// main.js
window.addEventListener("load", function load() {
window.removeEventListener("load", load, false);
},false);
// main.js
window.addEventListener("load", function load() {
window.removeEventListener("load", load, false);
},false);
window.addEventListener("load", function load() {
window.removeEventListener("load", load, false);
},false);
// main.js
self.addEventListener("install", function () {
// we’ll see…
},false);
// sw.js
window.addEventListener("load", function load() {
window.removeEventListener("load", load, false);
},false);
// main.js
self.addEventListener("install", function () {
// we’ll see…
},false);
// sw.js
self.addEventListener("load", function load() {
self.removeEventListener("load", load, false);
},false);
// main.js
self.addEventListener("install", function () {
// we’ll see…
},false);
// sw.js
self.addEventListener('install', (event) => {
// Do some stuff
});
// sw.js
self.addEventListener('install', (event) => {
// Do some stuff
});
// sw.js
es6!
self.addEventListener('install', (event) => {
// Do some stuff
});
// sw.js
Install
self.addEventListener('install', (event) => {
// Do some stuff
});
// sw.js
self.addEventListener('install', (event) => {
// Cache some stuff
});
// sw.js
Cache
window.name
document.cookie
Flash Cache
IndexedDB
localStorage
AppCache
Cache API
WebSQL
window.name
document.cookie
Flash Cache
IndexedDB
localStorage
AppCache
Cache API
WebSQL
window.name
document.cookie
Flash Cache
IndexedDB
localStorage
AppCache
Cache API
WebSQL
Cache
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
// Cache some stuff
});
// sw.js
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
// Cache some stuff
});
// sw.js
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
// Cache some stuff
});
// sw.js
// sw.js
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
// Cache some stuff
});
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
var CACHE_NAME = 'v1';
var toCache = [
'/',
'/css/main.css',
'/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(toCache);
})
);
cache.addAll(toCache);
cache.addAll(toCache);cache.addAll([toCache]);
cache.add('./url')
cache.match
cache.matchAll
cache.put
cache.keys
cache.delete
Fetch
// sw.js
Fetch-ing
Fetch
Triggered
var oReq = new XMLHttpRequest();
<img src="foo.jpg">
<link rel="stylesheet" href="./main.css">
Fetch
Triggered
Fetch
Triggerednavigator.serviceWorker.register('/sw.js')
body {
background: url('./bg.png')
} Fetch
Triggered
Easy Mode
Easy Mode
aka “Cache Only”
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
fetch
fetch
fetch('./url')
fetch('./url')
var request = new XMLHttpRequest();
request.addEventListener('load', function() {
console.log(this.responseText)
});
request.open('GET', './url', true);
request.send(null);
fetch('./url')
var request = new XMLHttpRequest();
request.addEventListener('load', function() {
console.log(this.responseText)
});
request.open('GET', './url', true);
request.send(null);
$.ajax('./url')
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => {
if (res) { return res; }
return fetch(event.request).then(res => {
cache.put(event.request, res.clone());
return res
});
}));
});
// sw.js
Medium Mode
aka “Cache and Update”
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => res))
event.waitUntil(
fetch(event.request).then(res => {
cache.put(event.request, res.clone())
.then(() => res);
});
);
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => res))
event.waitUntil(
fetch(event.request).then(res => {
cache.put(event.request, res.clone())
.then(() => res);
});
);
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => res))
event.waitUntil(
fetch(event.request).then(res => {
cache.put(event.request, res.clone())
.then(() => res);
});
);
});
// sw.js
self.addEventListener('fetch', event => {
event.respondWith(cache.match(event.request)
.then(res => res))
event.waitUntil(
fetch(event.request).then(res => {
cache.put(event.request, res.clone())
.then(() => res)
.then(refresh);
});
// sw.js
);
});
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
// sw.js
navigator.serviceWorker.register('/sw.js')
navigator.serviceWorker.register('/sw.js')
self.clients.matchAll()
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
.then(refresh);
});
function refresh(response) {
return self.clients.matchAll().then(clis => {
clis.forEach(client => {
var message = {
type: 'refresh',
url: response.url,
eTag: response.headers.get('ETag')
};
client.postMessage(JSON.stringify(message))
})
})
}
// main.js
}
// main.js
}
navigator.serviceWorker.onmessage =
function(ev) {
var message = JSON.parse(ev.data);
var lastETag = localStorage.currentETag;
if (lastETag !== message.eTag) {
// alert the user to reload the page
}
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
// main.js
}
navigator.serviceWorker.onmessage =
function(ev) {
var message = JSON.parse(ev.data);
var lastETag = localStorage.currentETag;
if (lastETag !== message.eTag) {
// alert the user to reload the page
}
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
// main.js
}
navigator.serviceWorker.onmessage =
function(ev) {
var message = JSON.parse(ev.data);
var lastETag = localStorage.currentETag;
if (lastETag !== message.eTag) {
// alert the user to reload the page
}
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
// main.js
}
navigator.serviceWorker.onmessage =
function(ev) {
var message = JSON.parse(ev.data);
var lastETag = localStorage.currentETag;
if (lastETag !== message.eTag) {
// alert the user to reload the page
}
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
// main.js
}
navigator.serviceWorker.onmessage =
function(ev) {
var message = JSON.parse(ev.data);
var lastETag = localStorage.currentETag;
if (lastETag !== message.eTag) {
// alert the user to reload the page
}
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
// main.js
}
navigator.serviceWorker.onmessage =
function(ev) {
var message = JSON.parse(ev.data);
var lastETag = localStorage.currentETag;
if (lastETag !== message.eTag) {
// alert the user to reload the page
}
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
And Beyond…
https://serviceworke.rs
Push Notification
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.text()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body
})
)
})
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body,
icon: "/img.jpg",
actions: {
action: true, title: "sure",
action: false, title: "nah"
}
// sw.js
self.addEventListener('push', function(event) {
var push = event.data.json()
event.waitUntil(
self.registration
.showNotification(push.title, {
body: push.body,
icon: "/img.jpg",
actions: {
action: true, title: "sure",
action: false, title: "nah"
}
// sw.js
https://tests.peter.sh/
notification-generator/
That’s just the begining

Contenu connexe

Tendances

Pianist and composer Jeff Kowalkowski releases strong new trio album
Pianist and composer Jeff Kowalkowski releases strong new trio albumPianist and composer Jeff Kowalkowski releases strong new trio album
Pianist and composer Jeff Kowalkowski releases strong new trio albumirwinvifxcfesre
 
Skaters and BMXers from all over the U.S. descend on Grant Park
Skaters and BMXers from all over the U.S. descend on Grant ParkSkaters and BMXers from all over the U.S. descend on Grant Park
Skaters and BMXers from all over the U.S. descend on Grant Parkchicagonewsyesterday
 
Android Fast Track CRUD Android PHP MySql
Android Fast Track CRUD Android PHP MySqlAndroid Fast Track CRUD Android PHP MySql
Android Fast Track CRUD Android PHP MySqlAgus Haryanto
 
Back To The Front - Javascript Test Driven Development is between us (workshop)
Back To The Front - Javascript Test Driven Development is between us (workshop)Back To The Front - Javascript Test Driven Development is between us (workshop)
Back To The Front - Javascript Test Driven Development is between us (workshop)Marco Cedaro
 
Here's the Downtown Sound lineup for 2015
Here's the Downtown Sound lineup for 2015Here's the Downtown Sound lineup for 2015
Here's the Downtown Sound lineup for 2015chicagonewsyesterday
 
WordPress-Themes mit Twig entwickeln
WordPress-Themes mit Twig entwickelnWordPress-Themes mit Twig entwickeln
WordPress-Themes mit Twig entwickelnWalter Ebert
 
Angular 2 не так уж и плох... А если задуматься, то и просто хорош / Алексей ...
Angular 2 не так уж и плох... А если задуматься, то и просто хорош / Алексей ...Angular 2 не так уж и плох... А если задуматься, то и просто хорош / Алексей ...
Angular 2 не так уж и плох... А если задуматься, то и просто хорош / Алексей ...Ontico
 
QCON SP 2014: 10 dicas de desempenho para apps mobile hibridas
QCON SP 2014: 10 dicas de desempenho para apps mobile hibridasQCON SP 2014: 10 dicas de desempenho para apps mobile hibridas
QCON SP 2014: 10 dicas de desempenho para apps mobile hibridasLoiane Groner
 
Javascript技巧参考大全
Javascript技巧参考大全Javascript技巧参考大全
Javascript技巧参考大全fgghyyfk
 
Device Orientation & WebSocket API
Device Orientation & WebSocket APIDevice Orientation & WebSocket API
Device Orientation & WebSocket APIComparto Web
 
jQuery sans jQuery
jQuery sans jQueryjQuery sans jQuery
jQuery sans jQuerygoldoraf
 
Chief Keef's hologram can't catch a break, and it's a win for Keef
Chief Keef's hologram can't catch a break, and it's a win for KeefChief Keef's hologram can't catch a break, and it's a win for Keef
Chief Keef's hologram can't catch a break, and it's a win for Keefchicagonewsonlineradio
 
Node.js Scalability Tips
Node.js Scalability TipsNode.js Scalability Tips
Node.js Scalability TipsLuciano Mammino
 

Tendances (20)

Web App Mvc
Web App MvcWeb App Mvc
Web App Mvc
 
Pianist and composer Jeff Kowalkowski releases strong new trio album
Pianist and composer Jeff Kowalkowski releases strong new trio albumPianist and composer Jeff Kowalkowski releases strong new trio album
Pianist and composer Jeff Kowalkowski releases strong new trio album
 
Lesson 01
Lesson 01Lesson 01
Lesson 01
 
Skaters and BMXers from all over the U.S. descend on Grant Park
Skaters and BMXers from all over the U.S. descend on Grant ParkSkaters and BMXers from all over the U.S. descend on Grant Park
Skaters and BMXers from all over the U.S. descend on Grant Park
 
Macdom html preprocesor
Macdom html preprocesorMacdom html preprocesor
Macdom html preprocesor
 
Android Fast Track CRUD Android PHP MySql
Android Fast Track CRUD Android PHP MySqlAndroid Fast Track CRUD Android PHP MySql
Android Fast Track CRUD Android PHP MySql
 
Prototype UI
Prototype UIPrototype UI
Prototype UI
 
Back To The Front - Javascript Test Driven Development is between us (workshop)
Back To The Front - Javascript Test Driven Development is between us (workshop)Back To The Front - Javascript Test Driven Development is between us (workshop)
Back To The Front - Javascript Test Driven Development is between us (workshop)
 
Here's the Downtown Sound lineup for 2015
Here's the Downtown Sound lineup for 2015Here's the Downtown Sound lineup for 2015
Here's the Downtown Sound lineup for 2015
 
WordPress-Themes mit Twig entwickeln
WordPress-Themes mit Twig entwickelnWordPress-Themes mit Twig entwickeln
WordPress-Themes mit Twig entwickeln
 
Zamyakin
ZamyakinZamyakin
Zamyakin
 
Angular 2 не так уж и плох... А если задуматься, то и просто хорош / Алексей ...
Angular 2 не так уж и плох... А если задуматься, то и просто хорош / Алексей ...Angular 2 не так уж и плох... А если задуматься, то и просто хорош / Алексей ...
Angular 2 не так уж и плох... А если задуматься, то и просто хорош / Алексей ...
 
QCON SP 2014: 10 dicas de desempenho para apps mobile hibridas
QCON SP 2014: 10 dicas de desempenho para apps mobile hibridasQCON SP 2014: 10 dicas de desempenho para apps mobile hibridas
QCON SP 2014: 10 dicas de desempenho para apps mobile hibridas
 
Javascript技巧参考大全
Javascript技巧参考大全Javascript技巧参考大全
Javascript技巧参考大全
 
Code
CodeCode
Code
 
Device Orientation & WebSocket API
Device Orientation & WebSocket APIDevice Orientation & WebSocket API
Device Orientation & WebSocket API
 
jQuery sans jQuery
jQuery sans jQueryjQuery sans jQuery
jQuery sans jQuery
 
Tugas uts
Tugas utsTugas uts
Tugas uts
 
Chief Keef's hologram can't catch a break, and it's a win for Keef
Chief Keef's hologram can't catch a break, and it's a win for KeefChief Keef's hologram can't catch a break, and it's a win for Keef
Chief Keef's hologram can't catch a break, and it's a win for Keef
 
Node.js Scalability Tips
Node.js Scalability TipsNode.js Scalability Tips
Node.js Scalability Tips
 

Service Workers

Notes de l'éditeur

  1. Added to webkit ri in 2008