Optimising PWA performance

Optimising PWA performance

Slides from out talk at Fluent 2018

97e38d2c6636968957c178ae61d86184?s=128

Michael Gooding

June 12, 2018
Tweet

Transcript

  1. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Optimising PWA’s Michael

    Gooding & Gareth Hughes
  2. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Intro Agenda 1.

    What is a PWA? 2. Measure performance of a PWA 3. Optimising App startup 4. Optimising within the App 5. Improve user experience
  3. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Who are we?

    Michael Gooding Gareth Hughes Intro Z S
  4. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 The app Intro

  5. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 What is a

    PWA?
  6. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 1: What

    is a PWA?
  7. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Avoid data theft

    and downtime by extending the security perimeter outside the data-center and protect from increasing frequency, scale and sophistication of web attacks. Section 1: What is a PWA? Easy to launch Nice load screens Work offline (kind of) Can consume space BUT …
  8. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Avoid data theft

    and downtime by extending the security perimeter outside the data-center and protect from increasing frequency, scale and sophistication of web attacks. Section 1: What is a PWA? Simple Searchable Sharable Adaptable
  9. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Avoid data theft

    and downtime by extending the security perimeter outside the data-center and protect from increasing frequency, scale and sophistication of web attacks. Section 1: What is a PWA? PWA = Web + App Normal website +
  10. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Avoid data theft

    and downtime by extending the security perimeter outside the data-center and protect from increasing frequency, scale and sophistication of web attacks. Section 1: What is a PWA? • A built in browser proxy • JavaScript based • Bonus of push notifications • Cache assets on start up • Use cache when no network • Error handle when no cache and no network • And more…..
  11. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Avoid data theft

    and downtime by extending the security perimeter outside the data-center and protect from increasing frequency, scale and sophistication of web attacks. Section 1: What is a PWA? • Even in challenging network conditions • Standard page load timings important • As well as usability timings • 60fps rendering • No Jank • Responsive touch inputs
  12. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Avoid data theft

    and downtime by extending the security perimeter outside the data-center and protect from increasing frequency, scale and sophistication of web attacks. Section 1: What is a PWA?
  13. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Avoid data theft

    and downtime by extending the security perimeter outside the data-center and protect from increasing frequency, scale and sophistication of web attacks. Section 1: What is a PWA? TLS is a ranking index in it’s own right But TLS opens access to: • Service workers • Push notification API • H2 for performance • Other API’s • Web Background sync • Payment API
  14. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Avoid data theft

    and downtime by extending the security perimeter outside the data-center and protect from increasing frequency, scale and sophistication of web attacks. Section 1: What is a PWA? • Adaptive is also OK • Applies to Tablets as well as mobile • Viewport <meta> tags • Correct sized content (think images) • Big enough buttons • Manual checks still useful
  15. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Avoid data theft

    and downtime by extending the security perimeter outside the data-center and protect from increasing frequency, scale and sophistication of web attacks. Section 1: What is a PWA?
  16. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Measure Performance of

    a PWA
  17. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measure

    performance of a PWA Agenda • Types of monitoring • Metrics and best practice • RUM lab
  18. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Types of monitoring

    Section 2: Measuring performance of a PWA
  19. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA Types of monitoring 1. Synthetic 1. Lighthouse 2. Webpagetest 3. Catchpoint / Rigor etc 2. Real User Monitoring 1. CRuX (free starting point) 2. mPulse (or other paid solution) 3. DIY
  20. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA Lighthouse – Dev Tools or Command line
  21. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA $ sudo npm install -g lighthouse $ lighthouse demo.pwa.akamaiworkshop.com
  22. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Demo Section 2:

    Measuring performance of a PWA
  23. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA Webpagetest
  24. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Demo Section 2:

    Measuring performance of a PWA
  25. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA Synthetic (Rigor)
  26. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA CrUX
  27. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA
  28. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Metrics and best

    practice Section 2: Measuring performance of a PWA
  29. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA Server 0.5s Total 10s Load 7s Render 2s Meaningful 4s
  30. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA ” Performance is for users”
  31. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA Real user Monitoring (mPulse)
  32. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Performance is for

    ALL users! Section 2: Measuring performance of a PWA
  33. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA Index.html ~ line 16 <!-- Bootstrap core CSS --> <link href="css/bootstrap.min.css" rel="stylesheet"> <!-- Custom styles for this template --> <link href="css/jumbotron.css" rel="stylesheet"> <script> performance.mark('CSS’); </script>
  34. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA Create user timing
  35. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA
  36. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab https://mpulselogin.soasta.com/ Username:

    [email address] Password: Fluent2018 Check data from your app Review dashboards Create a custom timing
  37. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 <break>coffee</break>

  38. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Optimising App startup

  39. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Agenda • Manifest • Icon • Hero image / carousel • CSS • JS • Fonts
  40. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Manifest Section 3:

    Optimising App startup
  41. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup https://www.pwabuilder.com/
  42. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 { "dir": "ltr",

    "lang": "English", "name": "Fluent Shop", "scope": "/", "display": "standalone", "start_url": "https://demo.pwa.akamaiworkshop.com/", "short_name": "Fluent Shop", "theme_color": "#93BE76", "description": "", "orientation": "any", "background_color": "#93BE76", "related_applications": [], "prefer_related_applications": false, "icons": [ { "src": "/img/icons/0ba00ee8-a3de-e472-3e4e-2a08830d5fb1.webPlatform.png", "sizes": "48x48", "type": "image/png" }, Manifest file Section 3: Optimising App startup
  43. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Be byte responsible

    • Cache the file • Compress the file • Consider renaming the images • Limit the number of images (2-5) • Cache those images • Optimise the images Section 3: Optimising App startup
  44. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Icons Section 3:

    Optimising App startup
  45. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Icon rendering on Moto G 24 x 24 300 x 300
  46. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup SVG & PNG on Moto G SVG PNG
  47. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Hero Images Section

    3: Optimising App startup
  48. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Hero Images and Carousels Hero Images and Carousels Defer Reduce weight
  49. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Reduce weight of

    images • Size * • Resize for device types • Resize for screen estate • Format • The basics (e.g. no png’s) • Browser specific formats • Quality • Baseline a good start • Intelligent automated solutions * See also responsive images Section 3: Optimising App startup
  50. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Defer loading of

    images • Page events • Object events • User events Section 3: Optimising App startup
  51. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Loading the correct

    image • Srcset & sizes • Simple size switching • Simple dpr switching • No mixing • Picture element • Combined support for • DPR • Size • Format Section 3: Optimising App startup
  52. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Reduce weight $ sudo apt-get install imagemagick [done] $ sudo apt-get install webp [done] $ cd var/www/html/img/carousel $ mkdir 400 800 1024 $ mogrify -path 400 -quality 80 -resize 400 *.jpg $ mogrify -path 400 -quality 80 -resize 400 -format webp *.jpg $ mogrify -path 800 -quality 80 -resize 800 *.jpg $ mogrify -path 800 -quality 80 -resize 800 -format webp *.jpg $ mogrify -path 1024 -quality 80 -resize 1024 *.jpg $ mogrify -path 1024 -quality 80 -resize 1024 -format webp *.jpg
  53. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Index.html ~ line 85 <div class="carousel-inner"> <div class="carousel-item active"> <a href="product.html?pid=004"> <img class="d-block w-100" src="/img/carousel/hero1.jpg" alt="First slide"> </a> </div> <div class="carousel-item"> ... </div> ... </div>
  54. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Index.html ~ line 88 <div class="carousel-item active"> <a href="product.html?pid=004"> <picture class="d-block w-100" alt="First slide" data-src=""> <source type="image/webp" srcset="/img/carousel/400/hero1.webp 400w, /img/carousel/800/hero1.webp 800w, /img/carousel/1024/hero1.webp 1024w" sizes="100vw"> <img src="/img/carousel/1024/hero1.jpg" srcset="/img/carousel/400/hero1.jpg 400w, /img/carousel/800/hero1.jpg 800w, /img/carousel/1024/hero1.jpg 1024w" sizes="100vw"> </picture> </a> </div>
  55. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup <div class="carousel-item"> <a href="product.html?pid=002"> <picture class="d-block w-100" data-src="hero2" alt="Second slide"> </picture> </a> </div> Index.html ~ line 93, 98 & 103
  56. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Index.html ~ line 48 <body onload="lazyload()"> function lazyload(){ var x = document.getElementsByClassName("d-block w-100"); var i; for (i = 0; i < x.length; i++) { if (x[i].getAttribute("data-src").length != 0) { var fileName = x[i].src = x[i].getAttribute("data-src"); var src = document.createElement("source"); src.type = "image/webp"; src.srcset = "/img/carousel/400/"+fileName+".webp 400w, /img/carousel/800/"+fileName+".webp 800w, /img/carousel/1024/"+fileName+".webp 1024w"; src.sizes = "100vw"; x[i].appendChild(src) var img = document.createElement("img"); img.src = "/img/carousel/1024/hero2.jpg"; img.srcset = "/img/carousel/400/"+fileName+".jpg 400w, /img/carousel/800/"+fileName+".jpg 800w, /img/carousel/1024/"+fileName+".jpg 1024w"; img.sizes = "100vw"; x[i].appendChild(img) } } }
  57. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Hero Images and Carousels Hero Images and Carousels Defer Reduce weight
  58. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Hero Images and Carousels Hero Images and Carousels Defered Image weight
  59. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Optimise your

    images Add a picture element Add lazy loading
  60. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 CSS Section 3:

    Optimising App startup
  61. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 CSS • Compress

    • Minify • Think about your approach… • Initial performance – think about the critical path • In-app performance & cacheability – big bang Section 3: Optimising App startup
  62. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Critical Path Faster rendering Smaller payload Improved app start-up time Only used once Maintenance overhead Monolith Reusable Increased offload Easy to maintain Can delay render Sending more bytes
  63. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup
  64. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup index.html home.min.css? Cache bootstrap.min.css Sw.js
  65. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup pwabuilder-sw.js var preLoad = function(){ console.log('[PWA Builder] Install Event processing'); return caches.open('pwabuilder-offline').then(function(cache) { console.log('[PWA Builder] Cached index and offline page during Install'); return cache.addAll( [ '/index.html', '/offline.html', '/img/offline.png', '/css/bootstrap.min.css', '/css/jumbotron.css', '/js/jquery-3.2.1.slim.min.js', '/js/vendor/popper.min.js', '/js/bootstrap.min.js' ]); }); }
  66. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup pwabuilder-sw.js self.addEventListener('fetch', function(event) { console.log('The service worker is serving the asset.'); if (event.request.url.match(/\.(css)$/)) { event.respondWith(returnFromCache(event.request).catch(function() { return checkResponse(event.request) })); } else { event.respondWith(checkResponse(event.request).catch(function() { return returnFromCache(event.request) })); } });
  67. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup pwabuilder-sw.js var returnFromCache = function(request){ return caches.open('pwabuilder-offline').then(function (cache) { return cache.match(request).then(function (matching) { if(!matching || matching.status == 404) { if (request.url.match(/\.(htm?l)$.*/)) { return cache.match('offline.html’) } if (request.url.match(/\.(jpe?g|png|gif|svg|webp)$/)) { return cache.match('/img/offline.png’) } if (request.url.match(/\.(css)$/)) { return cache.match('/css/bootstrap.min.css’) } } else { return matching } }); }); };
  68. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Swap in

    critical path CSS on the homepage Configure Service Worker to serve the monolith
  69. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup PUSH! • Push critical resources • Use the “dead” time • https://shouldipush.com • https://canipush.com • Can be used with either strategy
  70. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup /etc/apache2/sites-available/default-ssl.conf <LocationMatch ".*\.html"> Header add Link ”</css/bootstrap.min.css>;rel=preload;as=style" Header add Link "</css/jumbotron.css>;rel=preload;as=style" </LocationMatch>
  71. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Add Push

    hints to Apache Configuration
  72. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 JS Section 3:

    Optimising App startup
  73. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup JavaScript best practice • Compress • Cache • Minify Async Defer
  74. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 /critical.js /func.js /css

    /html Section 3: Optimising App startup Synchronously loading func.js dns connect ssl request response render render
  75. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 /critical.js /func.js /css

    /html Section 3: Optimising App startup dns connect ssl request response Asynchronously loading fun.js render render async
  76. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 /critical.js /func.js /css

    /html Section 3: Optimising App startup dns connect ssl request response Defer the loading & execution of func.js render render defer
  77. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Index.html ~ line 132 <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="js/jquery-3.2.1.slim.min.js"></script> <script src="js/vendor/popper.min.js"></script> <script src="js/bootstrap.min.js"></script> </body> </html>
  78. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Index.html ~ line 132 <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="js/jquery-3.2.1.slim.min.js" async></script> <script src="js/vendor/popper.min.js" defer></script> <script src="js/bootstrap.min.js" async></script> </body> </html>
  79. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Experiment adding

    DEFER / ASYNC attributes to JS on PLP / PDP
  80. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Fonts Section 3:

    Optimising App startup
  81. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup
  82. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup • Externally hosted • Self-hosted • Eliminate 3rd party on the render path • Greater control • Preload • Push
  83. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Add Preload

    header for Google Font CSS Change CSS to self-hosted fonts Push Font Files Preload in Service Worker
  84. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 <break>lunch</break>

  85. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Optimising within the

    App
  86. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App Agenda • Rendering with h2 • Image optimisation • Service worker for caching • Preload content
  87. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App Rendering (with h2)
  88. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App H2 Recap • Multiplexing • Header compression • Server push
  89. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App Rendering with h2
  90. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App
  91. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App
  92. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App H2 rendering best practice • Critical content from single domain • Defer non-critical content • Server push • Preload 3rd party content * * resource hints
  93. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App Single domain
  94. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App pushed
  95. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App preloaded
  96. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App Image optimisation
  97. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App PLP Images 200 x 200
  98. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App $ cd var/www/html/products/images $ mkdir thumbs thumbsLQ $ mogrify -path thumbs -colorspace RGB -quality 90 -resize 400 *_1.jpg $ mogrify -path thumbsLQ -colorspace RGB -quality 20 -resize 400 *.jpg
  99. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App <script id="prod-template" type="text/x-handlebars-template"> {{#each products}} <div class="col-md-4 text-center"> <img src="products/images/pid{{id}}_1.jpg" width="200px" /> <h4><a href="product.html#pid={{id}}">{{title}}</a></h4> <h3>${{price}}</h3> Products.html ~ line 58
  100. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App <script id="prod-template" type="text/x-handlebars-template"> {{#each products}} <div class="col-md-4 text-center"> <img src="products/images/thumbs/pid{{id}}_1.jpg" width="200px" /> <h4><a href="product.html#pid={{id}}">{{title}}</a></h4> <h3>${{price}}</h3> Products.html ~ line 58
  101. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App Quick size fix
  102. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App
  103. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App <script id="prod-template" type="text/x-handlebars-template"> {{#each products}} <div class="col-md-4 text-center"> <img data-src="products/images/thumbs/pid{{id}}_1.jpg" width="200px" /> <h4><a href="product.html#pid={{id}}">{{title}}</a></h4> <h3>${{price}}</h3> Products.html ~ line 58
  104. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App function showProducts(){ products = document.getElementsByClassName("col-md-4 text-center"); for (i = 0; i < products.length; i++) { if(i >= (firstProduct -1) && i <= (lastProduct -1)) { products[i].style.display = 'block'; } else { document.getElementsByClassName("col-md-4 text- center")[i].style.display = 'none'; } } } /js/pagination.js~ line 63
  105. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App function showProducts(){ ... for (i = 0; i < products.length; i++) { if(i >= (firstProduct -1) && i <= (lastProduct -1)) { products[i].style.display = 'block'; /* lazy load images */ var divID = products[i].id; var imgID = divID.replace("div_", "img_"); if(document.getElementById(imgID).src == ’’){ document.getElementById(imgID).src = document.getElementById(imgID).getAttribute("data-src"); } } else { /js/pagination.js~ line 63
  106. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App Lazy loading
  107. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Create variant

    images Update image source Add code to lazy load
  108. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App Service worker for caching
  109. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App What’s our Service Worker *doing*? …let’s take a look
  110. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/
  111. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/
  112. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Configure Service

    Worker to Cache every file accessed
  113. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App Let’s think about our native-like experience… • Browse the shop offline? • Add products to basket & purchase later
  114. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Add remaining

    pages to preload in Service Worker Discover & preload primary image and thumbs
  115. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 <break>coffee</break>

  116. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Improve user experience

  117. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience Agenda • 3rd parties • Offline measurement • Network Awareness • RAIL
  118. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience 3rd Parties
  119. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience
  120. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience
  121. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience SPOF
  122. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience SPOF
  123. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience SPOF <!-- Google Tag Manager --> <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-N5T323S');</script> <!-- End Google Tag Manager -->
  124. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience 3rd Party best practice • Remove them all
  125. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience 3rd Party best practice • Self host if possible • JS – async & defer where possible • Resource hints (preload & preconnect) • Measure performance • Measure impact • Impose SLA’s
  126. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience 3rd Party Monitoring
  127. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience 3rd Party Monitoring
  128. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience 3rd Party impact
  129. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience pwabuilder-sw.js self.addEventListener('fetch', function(event) { if(event.request.url.startsWith('https://demo.pwa.akamaiworkshop.com')) { event.respondWith(checkResponse(event.request).catch(function() { return returnFromCache(event.request) })); } else { event.respondWith(Promise.race([timeout(2000), fetch(event.request.url)])); } });
  130. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience pwabuilder-sw.js function timeout(delay) { return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(new Response('', { status: 408, statusText: 'Request timed out.’ })); }, delay); }); }
  131. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Create custom

    timer for 3rd party resource Introduce SLA’s for 3rd party content
  132. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience Offline Measurement
  133. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience
  134. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience • Google Analytics • https://garl.ink/offline-analytics • Collect & store offline • Replay when network available • We built a basic mPulse collector • Code Demo • Will be released • You can build your own!
  135. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Add Google

    Analytics offline support
  136. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience Network Awareness
  137. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience
  138. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience • Not great for someone on limited bandwidth • Consider the cost… • https://whatdoesmysitecost.com/
  139. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience What if we could know what type of connection or the user’s preference? • Act differently for desktop/mobile • Desktop more likely to be on a fixed (unmetered/fast) connection • Detect if user has Chrome Data Saver enabled • Intercept requests and rewrite them • Use the Network API to discover the type of connection • Currently Draft…
  140. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience pwabuilder-sw.js self.addEventListener('install', function(event) { if (navigator.userAgent.match(/(M|mobile).*$/)) { event.waituntil(minimalPreLoad()); } else { event.waituntil(preLoad()); } });
  141. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience pwabuilder-sw.js self.addEventListener('install', function(event) { if(navigator.connection.saveData) { event.waituntil(minimalPreLoad()); } else { event.waituntil(preLoad()); } });
  142. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience pwabuilder-sw.js self.addEventListener('fetch', function(event) { requestClone = event.request.clone(); if (navigator.connection.saveData && event.request.url.match(/(\/thumbs\/)/)) { return lowQualityImages(event.request); } event.respondWith(checkResponse(event.request).catch(function() { return returnFromCache(event.request)} )); }); var lowQualityImages = function(request) { //serve low quality images var url = request.url; newUrl = url.replace(/\/thumbs\//i, "/thumbsLQ/"); return fetch(newUrl); }
  143. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Add Network

    Awareness for Preload Add LQ Thumbnails for Data Saver
  144. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience pwabuilder-sw.js self.addEventListener('install', function(event) { if(navigator.connection && navigator.connection.type !== false) { //Network API data available if(navigator.connection.type == "ethernet" || "wifi" || "wimax") { //Fast, likely unmentered... preload all the things event.waituntil(preLoad()); } else { //Cellular or unable to detect... just give basic offline support event.waituntil(minimalPreLoad()); } } else { //Network API data not supported event.waitUntil(preLoad()); } });
  145. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience
  146. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience RAIL
  147. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience 4 Parts of RAIL Source: https://developers.google.com/web/fundamentals/performance/rail
  148. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience Response • Respond in under 50ms • Don’t block the user, make sure response is within 100ms • Provide feedback for anything else Source: https://developers.google.com/web/fundamentals/performance/rail
  149. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience Animation • Do nothing where you can, do the minimum where you can’t • Use the 100ms response to pre-calculate expensive work • Target 10ms/frame, 6ms to render • Consider all types • visual animation • scrolling • dragging Source: https://developers.google.com/web/fundamentals/performance/rail
  150. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience Idle • Use to complete deferred work • Keep work blocks <50ms to avoid blocking user input • User takes priority Source: https://developers.google.com/web/fundamentals/performance/rail
  151. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience Load Test in conditions that match your users • 4G doesn’t always mean 4G • Focus on critical rendering path • 5s doesn’t mean everything • Source: https://developers.google.com/web/fundamentals/performance/rail
  152. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience Measuring RAIL in Dev Tools • Throttle your CPU • Throttle the network • View main thread activity • View main thread activities in a table to sort • Analyze frames per second (FPS) • Monitor CPU usage, JS heap size, DOM nodes, layouts per second, and more • Visualize network requests • Capture screenshots • View interactions • Find scroll performance issues • View paint events Source: https://developers.google.com/web/fundamentals/performance/rail
  153. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Summary

  154. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Summary Summary PWA’s

    are 1. now mainstream Measure performance of a PWA 2. Optimise 3. App startup Optimise 4. within the App Consider 5. user experience
  155. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 </end>