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

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

Dean Hume

June 21, 2017
Tweet

More Decks by Dean Hume

Other Decks in Programming

Transcript

  1. • Is accessible on any device • Is fast •

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

    Works with little or no internet connection • Uses one codebase We needed an experience that:
  3. • 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:
  4. These apps aren’t packaged and deployed through stores, they’re just

    websites that took all the right vitamins. Alex Russell - Google “
  5. Think of your web apps requests as planes taking off.

    ServiceWorker is the air traffic controller that routes the requests. Jeff Posnick - Google “
  6. 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']); })); });
  7. 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() ); });
  8. 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); }) ); });
  9. 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
  10. 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
  11. this.addEventListener('fetch', event => { // Check if the user navigated

    if (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html')) { // Respond appropriately } };
  12. 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'); })); } };
  13. function timeout(delay) { return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(new

    Response('', { status: 408, statusText: 'Request timed out.' })); }, delay); }); }
  14. 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 => { });
  15. 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)])); });
  16. { 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" }
  17. { 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" }
  18. { 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" }
  19. Add to home screen 1. You need a manifest.json file

    with a start URL and 144x144 PNG icon
  20. 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
  21. 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.
  22. { 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" }
  23. H S