jquery uk february 2015: service workers on vacay

72
@thisNatasha ServiceWorkers on vacay... Natasha Rooney @thisNatasha GSMA Web Technologist W3C WebMob Co-Chair www.w3.org/Mobile/IG/

Upload: natasha-rooney

Post on 11-Jan-2017

442 views

Category:

Technology


0 download

TRANSCRIPT

@thisNatasha

ServiceWorkerson vacay...

Natasha Rooney@thisNatasha

GSMA Web TechnologistW3C WebMob Co-Chairwww.w3.org/Mobile/IG/

@thisNatasha

Going on Vacation

@thisNatasha

I use apps, but I want the web!

@thisNatasha

I want the web!

@thisNatasha

ServiceWorkers

@thisNatasha

ServiceWorker eh?

Offline Cache Worker

source: Chang W. Doh

@thisNatasha

ServiceWorker eh?

Offline Cache WorkerNavigation Controller

source: Chang W. Doh

@thisNatasha

ServiceWorker eh?

Offline Cache WorkerNavigation ControllerEvent Manager

source: Chang W. Doh

@thisNatasha

Offline Cache WorkerNavigation ControllerEvent Manager

ServiceWorker eh?

source: Chang W. Doh

@thisNatasha

ServiceWorker eh?

source: Chang W. Doh

Control request / response flowLoad a cached copy of a resource

@thisNatasha

ServiceWorker eh?

source: Chang W. Doh

Push & Background

Sync

Cache Control / Custom Response

Faster Load Time!

Control request / response flowLoad a cached copy of a resource

@thisNatasha

ServiceWorker eh?

source: Chang W. Doh

Control request / response flowLoad a cached copy of a resource

Push & Background

Sync

Cache Control / Custom Response

Faster Load Time!

@thisNatasha

Ok fine, but what is it?Worker: script

separate from the webpage

@thisNatasha

Ok fine, but what is it?Worker: script

separate from the webpageDifferent

Lifecycle

@thisNatasha

Ok fine, but what is it?Worker: script

separate from the webpageDifferent

LifecycleTerminated when

not in use

@thisNatasha

Ok fine, but what is it?Worker: script

separate from the webpageDifferent

Lifecycle

“In terms of network control, it acts like a proxy server sitting on the client, you get to decide what to do on a request-by-request basis. You can use this to make stuff work faster, offline, or build new features.”Jake Archibald

Terminated when not in use

@thisNatasha

LifecycleInstalls

Activates

Waits Network fetch detection

Terminated

source: html5rocks

@thisNatasha

Two APIs one interface representing a proxy for a value not necessarily known when that thing is created… (um, Promises)

FetchGives ServiceWorkers the powers to manage network requests and return responses for resources

https://fetch.spec.whatw

g.org/

https://slightlyoff.github.io/S

erviceWorker/spec/service_w

orker/index.htm

l#cache-objects

CacheSave fetched responses, return these responses later, not part of HTTP cache

+ Promises

@thisNatasha

Setup

@thisNatasha

Setup

@thisNatasha

Setup

40

@thisNatasha

@thisNatasha

@thisNatasha

Installation

Running [Status, Start, Stop]

Sync / Push

States:

@thisNatasha

Installation

Running [Status, Start, Stop]

Sync / Push

States:

@thisNatasha

Let’s do this.

@thisNatasha

RegisterRegister your ServiceWorker in your main JavaScript file.

// filename: app.js

if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(function(registration) { // Registration was successful console.log('ServiceWorker registration successful,

scope: ', registration.scope); }).catch(function(err) { // registration failed console.log('ServiceWorker registration failed: ', err); });}

source: html5rocks

@thisNatasha

RegisterRegister your ServiceWorker in your main JavaScript file.

// filename: app.js

if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(function(registration) { // Registration was successful console.log('ServiceWorker registration successful,

scope: ', registration.scope); }).catch(function(err) { // registration failed console.log('ServiceWorker registration failed: ', err); });}

source: html5rocks

app.js

@thisNatasha

RegisterRegister your ServiceWorker in your main JavaScript file.

// filename: app.js

if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(function(registration) { // Registration was successful console.log('ServiceWorker registration successful,

scope: ', registration.scope); }).catch(function(err) { // registration failed console.log('ServiceWorker registration failed: ', err); });}

source: html5rocks

SW location

@thisNatasha

RegisterRegister your ServiceWorker in your main JavaScript file.

// filename: app.js

if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(function(registration) { // Registration was successful console.log('ServiceWorker registration successful,

scope: ', registration.scope); }).catch(function(err) { // registration failed console.log('ServiceWorker registration failed: ', err); });}

source: html5rocks

Browser does the rest!

@thisNatasha

Create sw.jsOr whatever you want to call your ServiceWorker javascript file...

// filename: sw.js

TIP: sw.js goes in the root of the domain

@thisNatasha

InstallGive assets you want to run offline

// filename: sw.js

// The files we want to cachevar shellcache = 'shell-v1';var urlsToCache = [ '/', '/app/css/dist/bootstrap.min.css', '/app/js/dist/angular.min.js', '/app/js/dist/jquery-1.11.2.min.js', '/app/js/dist/bootstrap.min.js'];

// INSTALLself.addEventListener('install', function(event) { // Perform install steps

event.waitUntil(caches.open(shellcache) //our cache! .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); //pass all our urls! }));

});

@thisNatasha

// filename: sw.js

// The files we want to cachevar shellcache = 'shell-v1';var urlsToCache = [ '/', '/app/css/dist/bootstrap.min.css', '/app/js/dist/angular.min.js', '/app/js/dist/jquery-1.11.2.min.js', '/app/js/dist/bootstrap.min.js'];

// INSTALLself.addEventListener('install', function(event) { // Perform install steps

event.waitUntil(caches.open(shellcache) //our cache! .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); //pass all our urls! }));

});

InstallGive assets you want to run offline

Setup and populate caches!

@thisNatasha

// filename: sw.js

// The files we want to cachevar shellcache = 'shell-v1';var urlsToCache = [ '/', '/app/css/dist/bootstrap.min.css', '/app/js/dist/angular.min.js', '/app/js/dist/jquery-1.11.2.min.js', '/app/js/dist/bootstrap.min.js'];

// INSTALLself.addEventListener('install', function(event) { // Perform install steps

event.waitUntil(caches.open(shellcache) //our cache! .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); //pass all our urls! }));

});

Install

opened cache!

Give assets you want to run offline

@thisNatasha

// filename: sw.js

// The files we want to cachevar shellcache = 'shell-v1';var urlsToCache = [ '/', '/app/css/dist/bootstrap.min.css', '/app/js/dist/angular.min.js', '/app/js/dist/jquery-1.11.2.min.js', '/app/js/dist/bootstrap.min.js'];

// INSTALLself.addEventListener('install', function(event) { // Perform install steps

event.waitUntil(caches.open(shellcache) //our cache! .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); //pass all our urls! }));

});

Install

pass in array of files

Give assets you want to run offline

@thisNatasha

// filename: sw.js

// The files we want to cachevar shellcache = 'shell-v1';var urlsToCache = [ '/', '/app/css/dist/bootstrap.min.css', '/app/js/dist/angular.min.js', '/app/js/dist/jquery-1.11.2.min.js', '/app/js/dist/bootstrap.min.js'];

// INSTALLself.addEventListener('install', function(event) { // Perform install steps

event.waitUntil(caches.open(shellcache) //our cache! .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); //pass all our urls! }));

});

Install

TIP: everything must cache at

install to be successful!

Give assets you want to run offline

@thisNatasha

@thisNatasha

TIP: Use an incognito

window to test your app!

@thisNatasha

FetchDetect network fetches

// detect fetchesself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) {

}) );});

1st Visit: Install ServiceWorker

Later visits + navigation:

worker kicks in!

@thisNatasha

Fetch

// detect fetchesself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) {

}) );});

Worker will receive “fetch”

events

Detect network fetches

@thisNatasha

Fetch

// detect fetchesself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) {

}) );});

Worker will receive “fetch”

events

fetch API allows us to respond!

Detect network fetches

@thisNatasha

// detect fetchesself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) {

}) );});

Fetch

Worker will receive “fetch”

events

Manual response, Network fetch, or cached resouce!

Detect network fetches

@thisNatasha

Fetch

// detect fetchesself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) {

}) );});

Worker will receive “fetch”

events

gives info about request

Detect network fetches

@thisNatasha

// sw.jsself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { if (response) { return response; // cache hit! return response }

// IMPORTANT: Clone the request. var fetchRequest = event.request.clone();

return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic') { return response; }

// IMPORTANT: Clone the response. var responseToCache = response.clone();

caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); });

return response; } ); }) );});

CachingDetected fetches, return match if so or get from network as fallback

pass promisecaches.match

@thisNatasha

// sw.jsself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { if (response) { return response; // cache hit! return response }

// IMPORTANT: Clone the request. var fetchRequest = event.request.clone();

return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic') { return response; }

// IMPORTANT: Clone the response. var responseToCache = response.clone();

caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); });

return response; } ); }) );});

Caching

hmmm, any matches in my

caches?

Detected fetches, return match if so or get from network as fallback

@thisNatasha

Caching

// sw.jsself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { if (response) { return response; // cache hit! return response }

// IMPORTANT: Clone the request. var fetchRequest = event.request.clone();

return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic') { return response; }

// IMPORTANT: Clone the response. var responseToCache = response.clone();

caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); });

return response; } ); }) );});

Got it! Awesome, so return it!

Detected fetches, return match if so or get from network as fallback

@thisNatasha

Caching

// sw.jsself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { if (response) { return response; // cache hit! return response }

// IMPORTANT: Clone the request. var fetchRequest = event.request.clone();

return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic') { return response; }

// IMPORTANT: Clone the response. var responseToCache = response.clone();

caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); });

return response; } ); }) );});

Aw nothing in cache, get from

network

Detected fetches, return match if so or get from network as fallback

@thisNatasha

CachingGot it from the network? Why not add to the cache?

// sw.js return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic') { return response; }

// IMPORTANT: Clone the response. A response is a stream // and because we want the browser to consume the response // as well as the cache consuming the response, we need // to clone it so we have 2 stream. var responseToCache = response.clone();

caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); });

return response; } ); }) );});

@thisNatasha

CachingGot it from the network? Why not add to the cache?

// sw.js return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200

|| response.type !== 'basic') { return response; }

// IMPORTANT: Clone the response. A response is a stream // and because we want the browser to consume the response // as well as the cache consuming the response, we need // to clone it so we have 2 stream. var responseToCache = response.clone();

caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); });

return response; } ); }) );});

check response

@thisNatasha

CachingGot it from the network? Why not add to the cache?

// sw.js return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200

|| response.type !== 'basic') { return response; }

// IMPORTANT: Clone the response. A response is a stream // and because we want the browser to consume the response // as well as the cache consuming the response, we need // to clone it so we have 2 stream. var responseToCache = response.clone();

caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); });

return response; } ); }) );});

clone response

@thisNatasha

CachingGot it from the network? Why not add to the cache?

// sw.js return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200

|| response.type !== 'basic') { return response; }

// IMPORTANT: Clone the response. A response is a stream // and because we want the browser to consume the response // as well as the cache consuming the response, we need // to clone it so we have 2 stream. var responseToCache = response.clone();

caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); });

return response; } ); }) );});

add to cache

@thisNatasha

UpdateUpdating your ServiceWorker

// sw.jsself.addEventListener('activate', function(event) {

//var cacheWhitelist = ['shell-v1', 'blog-posts-cache-v1']; var cacheWhitelist = ['shell-v1'];

event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { console.log('deleting: ' + cacheName); return caches.delete(cacheName); } }) ); }) );});

@thisNatasha

UpdateUpdating your ServiceWorker

// sw.jsself.addEventListener('activate', function(event) {

//var cacheWhitelist = ['shell-v1', 'blog-posts-cache-v1']; var cacheWhitelist = ['shell-v1'];

event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { console.log('deleting: ' + cacheName); return caches.delete(cacheName); } }) ); }) );});

Edit and save me

@thisNatasha

UpdateUpdating your ServiceWorker

// sw.jsself.addEventListener('activate', function(event) {

//var cacheWhitelist = ['shell-v1', 'blog-posts-cache-v1']; var cacheWhitelist = ['shell-v1'];

event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { console.log('deleting: ' + cacheName); return caches.delete(cacheName); } }) ); }) );});

Edit and save me

TIP: completely close the

application!

@thisNatasha

Update

// sw.jsself.addEventListener('activate', function(event) {

//var cacheWhitelist = ['shell-v1', 'blog-posts-cache-v1']; var cacheWhitelist = ['shell-v1'];

event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { console.log('deleting: ' + cacheName); return caches.delete(cacheName); } }) ); }) );});

Updating your ServiceWorker

Open app and new SW should

kick in!

@thisNatasha

UpdateCheck caches need deleting or keeping

// sw.jsself.addEventListener('activate', function(event) {

//var cacheWhitelist = ['shell-v1', 'blog-posts-cache-v1']; var cacheWhitelist = ['shell-v1'];

event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { console.log('deleting: ' + cacheName); return caches.delete(cacheName); } }) ); }) );});

Let’s do some cache

management!

activate callback

source: html5rocks

@thisNatasha

UpdateCheck caches need deleting or keeping

// sw.jsself.addEventListener('activate', function(event) {

var cacheWhitelist = ['shell-v1', 'blog-posts-cache-v1'];

event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { console.log('deleting: ' + cacheName); return caches.delete(cacheName); } }) ); }) );});

Let’s do some cache

management!

defined in install step

source: html5rocks

@thisNatasha

UpdateCheck caches need deleting or keeping

// sw.jsself.addEventListener('activate', function(event) {

var cacheWhitelist = ['shell-v1', 'blog-posts-cache-v1'];

event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { console.log('deleting: ' + cacheName); return caches.delete(cacheName); } }) ); }) );});

Let’s do some cache

management!

loop through and delete!

source: html5rocks

@thisNatasha

Cool Other Stuff!

What more can I do?

Push / Background Sync

Mess around with fallbacks

Pass in weird and wonderful responses!

@thisNatasha

Ok I see how this works, anything else I

should know?

TIP: Github pages are

served over HTTPS

@thisNatasha

[1] ServiceWorkersmandate HTTPS

TIP: Github pages are

served over HTTPS

@thisNatasha

[2] So, can we use ServiceWorkers

today?

@thisNatasha

New Questions

http://caniuse.com/#feat=serviceworkers

@thisNatasha

New Questions

https://jakearchibald.github.io/isserviceworkerready/

@thisNatasha

How Can I Help?

@thisNatasha

https://github.com/w3c-webmob/ServiceWorkersDemos

@thisNatasha

https://code.google.com/p/chromium/issues/

@thisNatasha

http://discourse.specifiction.org/

@thisNatasha

New Questions

Super new! What else do we need to know!?

How does this scale?

Does SW improve performance?

How complex does development become?

@thisNatasha

Some things we couldn’t cover...

[1] Extensible Web Manifesto

[2] Impacted APIs

@thisNatasha

Info and Links

Info and Links

@thisNatasha

ありがとう!

Natasha Rooney@thisNatasha

GSMA Web TechnologistW3C WebMob Co-Chairwww.w3.org/Mobile/IG/

Thanks to:Matt Gaunt & Jake Archibald for their previous

written work on ServiceWorker!