Blink and you’ll miss it: Building a progressive web app with HTTP/2

Blink and you’ll miss it: Building a progressive web app with HTTP/2

This talk was first presented at Fluent Conference San Jose on the 21st June 2017.

https://conferences.oreilly.com/fluent/fl-ca/public/schedule/detail/58114

Presentation Details
----------------------------------
Imagine a world where you can access content on the web regardless of network connection in the blink of an eye. Progressive web apps are a total game changer for the web, enabling developers to build lightning-fast, engaging experiences. Drawing on firsthand experience at Settled, Dean Hume shares a step-by-step guide to implementing this technology in your own projects.

Settled set out to build a new dashboard for its customers using progressive web apps and HTTP/2. The company wanted to build an offline-first application that allowed users to quickly and easily access their information on the go regardless of their network connection. When the application was implemented, Settled noticed an immediate change: its users spent twice as long on the site, they consumed 15x less data, and enjoyed a 3x faster load times. Dean walks you through Settled’s journey, sharing lessons learned along the way and practical tips that you can use when building your own web apps.

Topics include:

- Using HTTP/2 and the challenges and benefits that it brings
- The steps Settled took when building an offline-first progressive web app
- Bulletproof service worker caching
- Building for sub-500 millisecond page-load times
- Eliminating third-party single points of failure
- Debugging challenges

C620790ae5bf5b50c245b2e0ef95f338?s=128

Dean Hume

June 21, 2017
Tweet

Transcript

  1. Blink and you’ll miss it Building a Progressive Web App

    with HTTP/2
  2. Dean Hume @deanohume

  3. I work for a company called Settled

  4. None
  5. Instead ➔ Transparent ➔ Online ➔ Everything to help you

    get “Settled” into your new home
  6. Our Customers 56%

  7. • Is accessible on any device We needed an experience

    that:
  8. • Is accessible on any device • Is fast We

    needed an experience that:
  9. • Is accessible on any device • Is fast •

    Works with little or no internet connection We needed an experience that:
  10. • Is accessible on any device • Is fast •

    Works with little or no internet connection • Uses one codebase We needed an experience that:
  11. • Is accessible on any device • Is fast •

    Works with little or no internet connection • Uses one codebase • Has an “app-like” feel We needed an experience that:
  12. Progressive Web Apps

  13. This is our journey

  14. This talk 1. Basics of progressive web apps

  15. This talk 1. Basics of progressive web apps 2. Fast

  16. This talk 1. Basics of progressive web apps 2. Fast

    3. Reliable
  17. This talk 1. Basics of progressive web apps 2. Fast

    3. Reliable 4. Look and feel
  18. The basics of Progressive Web Apps

  19. These apps aren’t packaged and deployed through stores, they’re just

    websites that took all the right vitamins. Alex Russell - Google “
  20. Building blocks Website

  21. Building blocks Website Service Worker

  22. Building blocks Website Service Worker Manifest File

  23. Building blocks Website HTTPS Service Worker Manifest File

  24. Think of your web apps requests as planes taking off.

    ServiceWorker is the air traffic controller that routes the requests. Jeff Posnick - Google “
  25. Behind the scenes

  26. With Service Workers

  27. With Service Workers

  28. With Service Workers

  29. With Service Workers

  30. Service Workers are the key to unlocking the power

  31. The perfect progressive enhancement.

  32. HTTPS Only

  33. Free SSL

  34. Support

  35. You are already using them

  36. 2. Fast

  37. Caching

  38. Caching HTTP/2

  39. Caching HTTP/2 Techniques

  40. Caching

  41. With Service Workers

  42. With Service Workers

  43. First Visit

  44. None
  45. None
  46. Register

  47. Register Install

  48. Register Install Activate

  49. Caching const cacheName = 'pageCache';

  50. Caching const cacheName = 'pageCache'; this.addEventListener('install', event => { event.waitUntil(caches.open(cacheName)

    .then(function(cache) { })); });
  51. Caching const cacheName = 'pageCache'; this.addEventListener('install', event => { event.waitUntil(caches.open(cacheName)

    .then(function(cache) { return cache.addAll(['result.min.css', './js/material.min.js']); })); });
  52. Caching const cacheName = 'pageCache'; this.addEventListener('install', event => { event.waitUntil(caches.open(cacheName)

    .then(function(cache) { return cache.addAll(['result.min.css', './js/material.min.js']); })); }); this.addEventListener('fetch', event => { event.respondWith(caches.match(event.request) .then() ); });
  53. Caching const cacheName = 'pageCache'; this.addEventListener('install', event => { event.waitUntil(caches.open(cacheName)

    .then(function(cache) { return cache.addAll(['result.min.css', './js/material.min.js']); })); }); this.addEventListener('fetch', event => { event.respondWith(caches.match(event.request) .then(function (response) { return response || fetch(event.request); }) ); });
  54. bit.ly/sw-toolbox

  55. 4.65 secs Page Load 2.25 secs 500 ms

  56. 4.65 secs Page Load 850 KB Page weight 50 KB

    1 KB 2.25 secs 500 ms
  57. HTTP/2

  58. source: blog.cloudflare.com

  59. source: blog.cloudflare.com

  60. HTTPS

  61. npm install spdy

  62. npm install spdy yarn add

  63. const spdy = require('spdy'); const express = require('express'); const app

    = express(); HTTP/2
  64. const spdy = require('spdy'); const express = require('express'); const app

    = express(); app.get('/home', (req, res) => { res.status(200).json({message: 'ok'}); }); spdy.createServer({ key: fs.readFileSync('./privatekey.key'), cert: fs.readFileSync('./certificate.crt') }, app) .listen(8012, (err) => { if (err) { throw new Error(err); } console.log('Listening on port: 8012.'); }); HTTP/2
  65. const spdy = require('spdy'); const express = require('express'); const app

    = express(); app.get('/home', (req, res) => { res.status(200).json({message: 'ok'}); }); spdy.createServer({ key: fs.readFileSync('./privatekey.key'), cert: fs.readFileSync('./certificate.crt') }, app) .listen(8012, (err) => { if (err) { throw new Error(err); } console.log('Listening on port: 8012.'); }); HTTP/2
  66. Client Side

  67. spdy: { protocols: [ 'h2', 'spdy/3.1', 'http/1.1' ], connection: {

    windowSize: 1024 * 1024, } } Config
  68. spdy: { protocols: [ 'h2', 'spdy/3.1', 'http/1.1' ], connection: {

    windowSize: 1024 * 1024, } } Config
  69. spdy: { protocols: [ 'h2', 'spdy/3.1', 'http/1.1' ], connection: {

    windowSize: 1024 * 1024, } } Config
  70. Techniques

  71. We used no JavaScript frameworks.

  72. We rendered the page server-side.

  73. We experimented with HTTP/2 Push.

  74. Without H2 Push

  75. With H2 Push

  76. None
  77. bit.ly/http2-jake

  78. H2 Push bit.ly/server-push-http2

  79. We also used prefetch.

  80. None
  81. None
  82. <link rel="prefetch" href="//example.com">

  83. Brotli = Better compression

  84. Brotli ~ 10%

  85. We tested using Lighthouse.

  86. Audit your Progressive Web App

  87. bit.ly/pwa-lighthouse

  88. 15 x less data

  89. 15 x less data 3x faster

  90. Caching HTTP/2 Techniques Summary

  91. None
  92. Offline

  93. With Service Workers

  94. this.addEventListener('fetch', event => { };

  95. this.addEventListener('fetch', event => { // Check if the user navigated

    if (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html')) { // Respond appropriately } };
  96. this.addEventListener('fetch', event => { // Check if the user navigated

    if (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html')) { // Respond appropriately event.respondWith( fetch(event.request.url).catch(error => { // Return the offline page return caches.match('the-offline-page'); })); } };
  97. None
  98. Network First

  99. Offline Applications bit.ly/offline-page

  100. Offline Analytics github.com/GoogleChrome/sw-helpers

  101. None
  102. Test your Service Worker code bit.ly/sw-testing

  103. 3rd Party Scripts

  104. Server Fails

  105. None
  106. Using Service Workers

  107. Using Service Workers

  108. With Service Workers

  109. function timeout(delay) { return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(new

    Response('', { status: 408, statusText: 'Request timed out.' })); }, delay); }); }
  110. function timeout(delay) { return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(new

    Response('', { status: 408, statusText: 'Request timed out.' })); }, delay); }); } self.addEventListener('fetch', event => { });
  111. function timeout(delay) { return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(new

    Response('', { status: 408, statusText: 'Request timed out.' })); }, delay); }); } self.addEventListener('fetch', event => { event.respondWith( Promise.race([timeout(6000), fetch(event.request.url)])); });
  112. bit.ly/sw-toolbox

  113. toolbox.router.get('/(.*)', global.toolbox.cacheFirst, { });

  114. toolbox.router.get('/(.*)', global.toolbox.cacheFirst, { origin: /\.google\.com$/, });

  115. toolbox.router.get('/(.*)', global.toolbox.cacheFirst, { origin: /\.google\.com$/, cache: { name: 'javascript', networkTimeoutSeconds:

    4 } });
  116. bit.ly/sw-timeout

  117. App-like Picture of mobile phones Look & Feel

  118. None
  119. App-like Picture of mobile phones Look & Feel ❖ Manifest

    file
  120. App-like Picture of mobile phones ❖ Manifest file ❖ Add

    to Home Screen Look & Feel
  121. Manifest A simple JSON file that allows you to control

    how your app appears to the user
  122. Look and feel

  123. { lang: "en", background_color: "#09adec", name: "Settled", short_name: "Settled Dashboard",

    display: "standalone", icons: [ { src: "./images/logo-144.png", sizes: "144x144", type: "image/png" } ], start_url: "/?start=a2hs", orientation: "portrait" }
  124. { lang: "en", background_color: "#09adec", name: "Settled", short_name: "Settled Dashboard",

    display: "standalone", icons: [ { src: "./images/logo-144.png", sizes: "144x144", type: "image/png" } ], start_url: "/?start=a2hs", orientation: "portrait" }
  125. { lang: "en", background_color: "#09adec", name: "Settled", short_name: "Settled Dashboard",

    display: "standalone", icons: [ { src: "./images/logo-144.png", sizes: "144x144", type: "image/png" } ], start_url: "/?start=a2hs", orientation: "portrait" }
  126. <!doctype html> <html lang="en"> <head> <link rel="manifest" href="manifest.json"> </head> <body>

    </body> </html>
  127. Manifest bit.ly/manifest-json

  128. Add to home screen

  129. Add to home screen 1. You need a manifest.json file

    with a start URL and 144x144 PNG icon
  130. Add to home screen 1. You need a manifest.json file

    with a start URL and 144x144 PNG icon 2. Your site is using a Service Worker running over HTTPS
  131. Add to home screen 1. You need a manifest.json file

    with a start URL and 144x144 PNG icon 2. Your site is using a Service Worker running over HTTPS 3. The user has visited your site at least twice, with at least five minutes between visits.
  132. { lang: "en", background_color: "#09adec", name: "Settled", short_name: "Settled Dashboard",

    display: "standalone", icons: [ { src: "./images/logo-144.png", sizes: "144x144", type: "image/png" } ], start_url: "/?start=a2hs", orientation: "portrait" }
  133. H S

  134. App-like Picture of mobile phones Summary

  135. App-like Picture of mobile phones Summary ❖ Fast

  136. App-like Picture of mobile phones Summary ❖ Fast ❖ Reliable

  137. App-like Picture of mobile phones Summary ❖ Fast ❖ Reliable

    ❖ Look & Feel
  138. We are still learning

  139. None
  140. Thank you! @deanohume