Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JS@PayPal 2015 - Insanely fast rendering w/ Service Workers and Early Flushing

Ec16728a8e4618dfd55b73dcc55c8beb?s=47 Mark Stuart
December 14, 2015

JS@PayPal 2015 - Insanely fast rendering w/ Service Workers and Early Flushing

Insanely fast rendering w/ Service Workers and Early Flushing

Ec16728a8e4618dfd55b73dcc55c8beb?s=128

Mark Stuart

December 14, 2015
Tweet

Transcript

  1. Insanely Fast Rendering! w/ Early Flushing and Service Workers JS@PayPal

    Winter 2015 - 12/14
  2. go/serviceworkers

  3. Hi, I’m Mark! @mark_stuart @marstuart

  4. Let's talk about pushing pixels to the browser as fast

    as possible.
  5. Early Flushing

  6. By default, HTTP responses are buffered.

  7. None
  8. None
  9. Browsers can’t render anything until that 1st HTML response is

    received. :(
  10. OK, so what can we do about it?

  11. What if we chunked up that HTML response into multiple

    parts?
  12. None
  13. Browsers can start downloading and parsing critical JS and CSS

    before the HTML response is finished.
  14. No Early Flush DOMLoaded- 3.5s Load - 3.8s First paint

    - 3.4s Early Flush DOMLoaded- 3.4s Load - 3.5s First paint - 695ms Saved 2.8s!
  15. Good news! You can use this too!

  16. Add a few headers… Flush out your scripts, styles, etc.

    Call your services, resolve locale, whatever else, then flush again. ! ! ! 1 res.setHeader('Transfer-Encoding', 'chunked'); 2 3 // For Slingshot... 4 res.setHeader('X-SLR-EARLY-FLUSH', '1'); 5 6 // For Akamai... 7 res.setHeader('X-Akamai-Stream', 'True'); res.write(renderTemplate('first-flush.html', context)); res.write(renderTemplate('second-flush.html', context)); res.end();
  17. Try it out! Just a few headers. Took a lot

    of work to pull this off. Ready for production after moratorium.
  18. With early flushing, we render at 695ms. But, that’s still

    not fast enough. I’d like to introduce you to Service Workers!
  19. Flush early!

  20. Service Workers

  21. Apps that use Service Workers today - Facebook - Push

    notifications - Pinterest - Push notifications - Medium - Caching - Google’s Inbox app - Caching & Push notifications
  22. Browser Support - Chrome 40+ - Firefox 44+ - Opera

    24+ - Android 46 - Safari and IE to come later.
  23. What’s a Service Worker?

  24. - Similar to a Web Worker - Can’t directly access

    the DOM - Communicates w/ parent by postMessage - Can import scripts via importScripts - Promise-based APIs - Easy debugging
  25. None
  26. Features - Able to intercept and handle network requests -

    Complete control of the browser’s cache - Push notifications - Background sync - Geofencing
  27. Most exciting feature? Being able to intercept and handle network

    requests!
  28. A few interesting use cases

  29. Serving assets from cache self.addEventListener('fetch', function (event) { event.respondWith( caches.open('my-cool-cache').then(function

    (cache) { return cache.match(event.request).then(function (response) { if (response) { return response; } return fetch(event.request.clone()).then(function (response) { if (response.status < 400) { cache.put(event.request, response.clone()); } return response; }); }); }); ); }); ! ! !
  30. Enforce SLA on 3rd party scripts ! ! ! self.addEventListener('fetch',

    function (event) { // If it's a 3rd party script, enforce a 2s SLA, then timeout if (thirdPartyScripts.includes(event.request.url)) { return event.respondWith(Promise.race([ timeout(2000), fetch(event.request.url) ])); } event.respondWith(fetch(event.request.url)); }); // FPTI and Fraudnet scripts var thirdPartyScripts = [ 'https://www.paypalobjects.com/pa/js/pa.js', 'https://www.paypalobjects.com/webstatic/r/fb/fb-all-prod.pp.min.js' ]; function timeout(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(new Response('', { status: 408, statusText: 'Request timed out' })); }, delay); }); }
  31. Enforce SLA on 3rd party scripts ! ! ! //

    FPTI and Fraudnet scripts var thirdPartyScripts = [ 'https://www.paypalobjects.com/pa/js/pa.js', 'https://www.paypalobjects.com/webstatic/r/fb/fb-all-prod.pp.min.js' ]; function timeout(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(new Response('', { status: 408, statusText: 'Request timed out' })); }, delay); }); } self.addEventListener('fetch', function (event) { // If it's a 3rd party script, enforce a 2s SLA, then timeout if (thirdPartyScripts.includes(event.request.url)) { return event.respondWith(Promise.race([ timeout(2000), fetch(event.request.url) ])); } event.respondWith(fetch(event.request.url)); });
  32. Supporting WebP images ! ! ! <img src="icon-sprite.png" /> self.addEventListener('fetch',

    function (event) { var request = event.request; var url = request.url; var isImage = /\.jpg$|.png$/.test(request.url); var supportsWebp = request.headers.get('accept').includes('webp'); if (isImage && supportsWebp) { url = request.url.substr(0, request.url.lastIndexOf('.')) + '.webp'; } return event.respondWith(fetch(url)); });
  33. Responsive images ! ! ! <img src="icon-sprite.png" /> Use images

    like you normally would… 1. Intercept that image request 2. If “retina” support (or device-pixel-ratio >= 2), re-write the URL as “icon-sprite@2x.png” 3. Request that from the network
  34. Defer requests while offline ! ! ! If a network

    request fails, or user goes offline… You can cache requests and re-play them when the user goes back online.
  35. Mock Server? lol ! ! ! Might be a really

    crazy idea, but it’s possible! Need to run in mocks mode? Use a Service Worker! Intercept API responses and send back mock responses No need to run a server
  36. Service Workers are great for PayPal use cases - Offline

    support - Send/Request money (P2P) - Rendering shells - Dashboard apps like Hawk and 8Ball all have this “shell” on the ourside that could be instantly rendered while the remaining bits are loaded afterwards.
  37. Application Shell Architecture - Coined by Addy Osmani, from the

    Chrome team - With Service Workers, you can instantly load your page on repeat visits. - Render the Shell or Chrome of your page immediately. - Lazy load in the rest of your page
  38. None
  39. Our Caching Strategy (Starting Q1) - Always read static/framework assets

    from cache (jQuery, Angular, etc.) - Always immediately render "shell" from cache - Fetch from network, by default. If success, cache response. - If any network issues or fallbacks, read from cache. - Add a 2 second SLA on 3rd party scripts
  40. Questions? #serviceworkers on Node.js Slack (nodejs.slack.com)