Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Optimising PWA performance

Optimising PWA performance

Slides from out talk at Fluent 2018

Michael Gooding

June 12, 2018
Tweet

More Decks by Michael Gooding

Other Decks in Technology

Transcript

  1. 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
  2. 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 …
  3. 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
  4. 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 +
  5. 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…..
  6. 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
  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?
  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? 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
  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? • 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
  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?
  11. 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
  12. 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
  13. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA Lighthouse – Dev Tools or Command line
  14. 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
  15. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Metrics and best

    practice Section 2: Measuring performance of a PWA
  16. 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
  17. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 2: Measuring

    performance of a PWA ” Performance is for users”
  18. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Performance is for

    ALL users! Section 2: Measuring performance of a PWA
  19. 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>
  20. 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
  21. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup Agenda • Manifest • Icon • Hero image / carousel • CSS • JS • Fonts
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Defer loading of

    images • Page events • Object events • User events Section 3: Optimising App startup
  28. 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
  29. 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
  30. 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>
  31. 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>
  32. 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
  33. 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) } } }
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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' ]); }); }
  40. 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) })); } });
  41. 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 } }); }); };
  42. 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
  43. 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
  44. 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>
  45. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 3: Optimising

    App startup JavaScript best practice • Compress • Cache • Minify Async Defer
  46. 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
  47. 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
  48. 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
  49. 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>
  50. 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>
  51. 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
  52. 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
  53. 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
  54. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 4: Optimising

    within the App H2 Recap • Multiplexing • Header compression • Server push
  55. 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
  56. 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
  57. 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
  58. 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
  59. 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
  60. 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
  61. 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
  62. 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
  63. 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/
  64. 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/
  65. 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
  66. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Add remaining

    pages to preload in Service Worker Discover & preload primary image and thumbs
  67. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience Agenda • 3rd parties • Offline measurement • Network Awareness • RAIL
  68. 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 -->
  69. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Section 5: Improving

    user experience 3rd Party best practice • Remove them all
  70. 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
  71. 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)])); } });
  72. 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); }); }
  73. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Create custom

    timer for 3rd party resource Introduce SLA’s for 3rd party content
  74. 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!
  75. 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/
  76. 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…
  77. 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()); } });
  78. 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()); } });
  79. 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); }
  80. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81 Lab Add Network

    Awareness for Preload Add LQ Thumbnails for Data Saver
  81. 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()); } });
  82. 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
  83. 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
  84. 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
  85. 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
  86. 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
  87. 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
  88. 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