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

LDNWebPerf March 2017 - Dean Hume

LDNWebPerf March 2017 - Dean Hume

As web developers, we’ve never been luckier. We have amazing technologies such as HTTP/2, Progressive Web Apps, and easy access to the Cloud, just to name a few. With these technologies at our fingertips, we are able to build fast, engaging and resilient web apps for our users.

At Settled, we set out to build a new dashboard for our customers using the features of Progressive Web Apps and HTTP/2. We wanted to build an offline-first application that allowed our users to quickly and easily access their information on the go regardless of their network connection. We noticed an immediate change; our users spent twice as long on the site, they consumed 15x less data, and enjoyed a 3x faster load times. In this presentation, we’ll cover our journey and the lessons that we learnt along the way as well as many real-world, practical tips that you can use when building your own web apps.

More Decks by London Web Performance Group

Other Decks in Technology

Transcript

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

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

    Works with little or no internet connection • Used one codebase We needed an experience that:
  3. • Was accessible on any device • Was fast •

    Works with little or no internet connection • Used one codebase • Had 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 “
  5. Think of your web apps requests as planes taking off.

    ServiceWorker is the air traffic controller that routes the requests. Jeff Posnick “
  6. 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); }) ); }); Caching
  7. ★ Fresh visit - 2 secs ★ Cached visit -

    500 ms ★ Less load on the server
  8. const spdy = require('spdy'); const express = require('express'); const app

    = express(); // Any incoming requests app.get('/home', (req, res) => { res.status(200).json({message: 'ok'}); }); HTTP/2
  9. const spdy = require('spdy'); const express = require('express'); const app

    = express(); // Any incoming requests app.get('/home', (req, res) => { res.status(200).json({message: 'ok'}); }); // Create the Server 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. this.addEventListener('fetch', event => { // Check if the user navigated

    if (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html')) { // Respond appropriately } };
  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 event.respondWith( fetch(event.request.url).catch(error => { // Return the offline page return caches.match('the-offline-page'); })); } };
  12. function timeout(delay) { return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(new

    Response('', { status: 408, statusText: 'Request timed out.' })); }, delay); }); }
  13. 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 => { });
  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 => { event.respondWith( Promise.race([timeout(6000), fetch(event.request.url)])); });
  15. { 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" }
  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. Add to home screen 1. You need a manifest.json file

    2. Your manifest file needs a start URL
  19. Add to home screen 1. You need a manifest.json file

    2. Your manifest file needs a start URL 3. You need a 144x144 PNG icon
  20. Add to home screen 1. You need a manifest.json file

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

    2. Your manifest file needs a start URL 3. You need a 144x144 PNG icon 4. Your site is using a Service Worker running over HTTPS 5. 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