Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Service Workers for Performance

6,792 views

Published on

Service Workers presentation from the 2015 Velocity Conference

Published in: Technology

Service Workers for Performance

  1. 1. Service Workers The Practical Bits @patmeenan Patrick Meenan
  2. 2. Slides Slideshare: http://www.slideshare.net/patrickmeenan Twitter: @patmeenan (and to #velocityconf) Velocity Site, attached to session information Video: https://www.youtube.com/user/patmeenan Office Hours: Friday, May 29 from 12:30pm-1:00pm @patmeenan
  3. 3. Chrome Team @Google (performance) WebPageTest pmeenan@webpagetest.org @patmeenan
  4. 4. The Real Experts Alex Russell @slightlylate Jake Archibald @jaffathecake
  5. 5. Service Workers
  6. 6. DOM Resource Fetching in Chrome
  7. 7. DOM Resource Fetching in Chrome In-memory Resource Cache
  8. 8. DOM Resource Fetching in Chrome In-memory Resource Cache Resource Fetcher
  9. 9. DOM Resource Fetching in Chrome Resource Fetcher Net Stack
  10. 10. DOM Resource Fetching in Chrome In-memory Resource Cache Resource Fetcher Net Stack Disk Cache
  11. 11. DOM Resource Fetching in Chrome In-memory Resource Cache Resource Fetcher Net Stack Net Disk Cache
  12. 12. DOM Resource Fetching in Chrome In-memory Resource Cache Resource Fetcher Net Stack Net Disk Cache Service Worker SW Added and Removed Here!
  13. 13. Capabilities Sees every request for your document - Including cross-origin - And headers Can synthesize responses Supports fetch Has a programmable cache and Indexed DB
  14. 14. Limitations HTTPS documents only Not active for first view iFrames are separate documents Non-CORS Cross-origin responses are opaque No global state - or concept of a “page” No Sync API’s (localstorage, XHR, etc)
  15. 15. http://memegenerator.net/instance/62336209
  16. 16. Registering if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/my-app/sw.js', { scope: '/my-app/' }); } https://github.com/slightlyoff/ServiceWorker/blob/master/explainer.md
  17. 17. Registering if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/my-app/sw.js', { scope: '/my-app/' }); } https://github.com/slightlyoff/ServiceWorker/blob/master/explainer.md
  18. 18. Registering if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/my-app/sw.js', { scope: '/my-app/' }); } https://github.com/slightlyoff/ServiceWorker/blob/master/explainer.md
  19. 19. Registering if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/my-app/sw.js', { scope: '/my-app/' }); } https://github.com/slightlyoff/ServiceWorker/blob/master/explainer.md
  20. 20. Install / activate self.addEventListener('install', function(event) { event.waitUntil( fetchStuffAndInitDatabases() ); }); self.addEventListener('activate', function(event) { // You're good to go! }); https://github.com/slightlyoff/ServiceWorker/blob/master/explainer.md
  21. 21. Network Intercepting self.addEventListener('fetch', function(event) { event.respondWith(new Response("Hello world!")); }); https://github.com/slightlyoff/ServiceWorker/blob/master/explainer.md
  22. 22. Enough Theory – Let’s Use It
  23. 23. Offline Content  Most benefit for Single-Page Apps where app/data are separated  Service Workers become progressive enhancement for offline support
  24. 24. Offline Content  Pre-cache offline content  Pass live requests through when online - Caching the responses  Serve cached responses when offline (if available)  Serve offline-specific versions otherwise
  25. 25. Offline Content – Pre-caching self.addEventListener( ‘install’, function(event) { event.waitUntil( caches.open(‘my-offline-cache-v1’).then( function(cache) { return cache.addAll([ ‘/site.js’, ‘/images/offline.png’, ‘/offline.html’]); }));});
  26. 26. Offline Content – Pre-caching self.addEventListener( ‘install’, function(event) { event.waitUntil( caches.open(‘my-offline-cache-v1’).then( function(cache) { return cache.addAll([ ‘/site.js’, ‘/images/offline.png’, ‘/offline.html’]); }));});
  27. 27. Offline Content – Pre-caching self.addEventListener( ‘install’, function(event) { event.waitUntil( caches.open(‘my-offline-cache-v1’).then( function(cache) { return cache.addAll([ ‘/site.js’, ‘/images/offline.png’, ‘/offline.html’]); }));});
  28. 28. Offline Content – Pre-caching self.addEventListener( ‘install’, function(event) { event.waitUntil( caches.open(‘my-offline-cache-v1’).then( function(cache) { return cache.addAll([ ‘/site.js’, ‘/images/offline.png’, ‘/offline.html’]); }));});
  29. 29. Offline Content – Pre-caching self.addEventListener( ‘install’, function(event) { event.waitUntil( caches.open(‘my-offline-cache-v1’).then( function(cache) { return cache.addAll([ ‘/site.js’, ‘/images/offline.png’, ‘/offline.html’]); }));});
  30. 30. Offline Content – Fetch Processing self.addEventListener( 'fetch', function(event) { if (navigator.online) { event.respondWith( onlineRequest(event.request) ); } else { event.respondWith( offlineRequest(event.request) ); } });
  31. 31. Offline Content – Fetch Processing self.addEventListener( 'fetch', function(event) { if (navigator.online) { event.respondWith( onlineRequest(event.request) ); } else { event.respondWith( offlineRequest(event.request) ); } });
  32. 32. Offline Content – Fetch Processing self.addEventListener( 'fetch', function(event) { if (navigator.online) { event.respondWith( onlineRequest(event.request) ); } else { event.respondWith( offlineRequest(event.request) ); } });
  33. 33. Offline Content – Fetch Processing self.addEventListener( 'fetch', function(event) { if (navigator.online) { event.respondWith( onlineRequest(event.request) ); } else { event.respondWith( offlineRequest(event.request) ); } });
  34. 34. Offline Content – Fetch Processing self.addEventListener( 'fetch', function(event) { if (navigator.online) { event.respondWith( onlineRequest(event.request) ); } else { event.respondWith( offlineRequest(event.request) ); } });
  35. 35. Offline Content – Online Response function onlineRequest(request) { return caches.match(request) .then(function(response) { … } ); }
  36. 36. Offline Content – Online Response function onlineRequest(request) { return caches.match(request).then(function(response) { if (response) { return response; } else { return fetch(request); } }); }
  37. 37. Offline Content – Online Response function onlineRequest(request) { return caches.match(request).then(function(response) { if (response) { return response; } else { return fetch(request); } }); }
  38. 38. Offline Content – Online Response … var fetchRequest = request.clone(); return fetch(fetchRequest).then( function(response) { var responseToCache = response.clone(); caches.open(CACHE_NAME).then( function(cache) { cache.put(request, responseToCache); }); return response; });
  39. 39. Offline Content – Online Response … var fetchRequest = request.clone(); return fetch(fetchRequest).then( function(response) { var responseToCache = response.clone(); caches.open(CACHE_NAME).then( function(cache) { cache.put(request, responseToCache); }); return response; });
  40. 40. Offline Content – Online Response … var fetchRequest = request.clone(); return fetch(fetchRequest).then( function(response) { var responseToCache = response.clone(); caches.open(CACHE_NAME).then( function(cache) { cache.put(request, responseToCache); }); return response; });
  41. 41. Offline Content – Online Response … var fetchRequest = request.clone(); return fetch(fetchRequest).then( function(response) { var responseToCache = response.clone(); caches.open(CACHE_NAME).then( function(cache) { cache.put(request, responseToCache); }); return response; });
  42. 42. Offline Content – Online Response … var fetchRequest = request.clone(); return fetch(fetchRequest).then( function(response) { var responseToCache = response.clone(); caches.open(CACHE_NAME).then( function(cache) { cache.put(request, responseToCache); }); return response; });
  43. 43. Offline Content – Online Response … var fetchRequest = request.clone(); return fetch(fetchRequest).then( function(response) { var responseToCache = response.clone(); caches.open(CACHE_NAME).then( function(cache) { cache.put(request, responseToCache); }); return response; });
  44. 44. Offline Content – Offline Response function offlineRequest(request) { if (request.url.match(/.png$/)) { return caches.match(‘/images/offline.png’); } else if (request.url.match(/.html$/)) { return caches.match(‘/offline.html’); } … }
  45. 45. Offline Content – Offline Response function offlineRequest(request) { if (request.url.match(/.png$/)) { return caches.match(‘/images/offline.png’); } else if (request.url.match(/.html$/)) { return caches.match(‘/offline.html’); } … }
  46. 46. Other Uses Not limited to applications designed for offline/SPA Legacy apps + fetch intercept = Awesome possibilities
  47. 47. Custom Error Pages Fetch Request Normally For non-200 responses serve a local error page Not just server errors: - DNS failures - CDN/Proxy Errors - Intermediaries
  48. 48. SPOF Prevention Identify requests of interest - 3rd -party javascript - Fonts Set a timer Pass fetch requests through If timer expires before fetch completes generate error response
  49. 49. CDN/Origin Failover Identify CDN requests by URL Set a timer Pass fetch request through If timer expires, create fetch request to origin Respond with first fetch request to complete
  50. 50. Multi-Origin/CDN Identify CDN requests by URL Replace CDN domain with alternate CDN (or origin) Keep track of performance by origin Prefer faster origin (keep measuring) Could also race the CDNs in parallel - Be careful about increased data
  51. 51. Stale-While-Revalidate Respond with local cached resource - Ignoring Expires, max-age, etc. Pass fetch request through in parallel Update cache with new response Works best for known resources (analytics/ads JS, etc)
  52. 52. Prefetch Custom response headers with prefetch resources When idle, prefetch suggested resources
  53. 53. Resource Prioritization/Scheduling Track in-flight requests Make scheduling decisions as new requests come in Custom application-aware scheduling logic - Delay JS if it is known to be at the end - Delay footer images - etc
  54. 54. Delta Compression Delta compression for build->build updates - Include version in URL scheme - Get latest version number from cache - Request delta from server - Apply patch - Cache new version New compression algorithms
  55. 55. Custom Compression New compression algorithms - Brotli - Fractal image compression - JSON-specific dictionaries - Application-specific Prove-out new algorithms before standardizing …as long as it can be implemented in JS and code size is reasonable
  56. 56. Incremental Progressive Images Identify JPEG image requests from known origin Synthesize response Range request (or smarter) for first few scans Stream initial range into synthesized response Range request for remaining image (some point later) Append remaining data into synthesized response
  57. 57. Drawing Images Locally  930 x 11,362 px WebPageTest waterfall  351KB compressed PNG  42 MB in-memory (server) to generate  < 20KB compressed JSON data to describe  Prefer actual images for WPT waterfalls for easier embedding - Otherwise SVG, Canvas or DOM would work
  58. 58. Drawing Images Locally* Identify appropriate requests (i.e. waterfall.png?...) Fetch data necessary to draw image Draw to canvas Extract as PNG Synthesize PNG response * Work in progress, coming soon
  59. 59. Metrics/Debugging Pass all requests through Passively observe timings and success Report metrics back Gives visibility into failures and requests that are in flight - Unlike resource timing
  60. 60. And there’s more…
  61. 61. Required for… Push Notifications - https://gauntface.com/blog/2014/12/15/push-notifications-service-worker Background Sync - https://github.com/slightlyoff/BackgroundSync/blob/master/explainer.md GeoFencing - https://github.com/slightlyoff/Geofencing
  62. 62. Availability Chrome Stable (40+) Opera (27+) Firefox (Nightly)
  63. 63. Composable Service workers can “importScripts” Begging for a framework to handle pluggable pipeline - With compatible plugins for processing - Delta compression + scheduling + Stale While Revalidate… - Just a matter of importing the libs
  64. 64. Thank You! @patmeenan pmeenan@webpagetest.org

×