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

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
    Optimising PWA’s
    Michael Gooding & Gareth Hughes

    View Slide

  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

    View Slide

  3. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Who are we?
    Michael Gooding Gareth Hughes
    Intro
    Z S

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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 …

    View Slide

  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

    View Slide

  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
    +

    View Slide

  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…..

    View Slide

  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

    View Slide

  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?

    View Slide

  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

    View Slide

  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 tags
    • Correct sized content (think images)
    • Big enough buttons
    • Manual checks still useful

    View Slide

  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?

    View Slide

  16. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Measure Performance of a PWA

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  28. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Metrics and best practice
    Section 2: Measuring performance of a PWA

    View Slide

  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

    View Slide

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

    View Slide

  31. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 2: Measuring performance of a PWA
    Real user Monitoring (mPulse)

    View Slide

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

    View Slide

  33. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 2: Measuring performance of a PWA
    Index.html ~ line 16




    <br/>performance.mark('CSS’);<br/>

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  37. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    coffee

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  41. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 3: Optimising App startup
    https://www.pwabuilder.com/

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  46. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 3: Optimising App startup
    SVG & PNG on Moto G
    SVG
    PNG

    View Slide

  47. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Hero Images
    Section 3: Optimising App startup

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  53. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 3: Optimising App startup
    Index.html ~ line 85



    alt="First slide">



    ...

    ...

    View Slide

  54. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 3: Optimising App startup
    Index.html ~ line 88



    srcset="/img/carousel/400/hero1.webp 400w,
    /img/carousel/800/hero1.webp 800w,
    /img/carousel/1024/hero1.webp 1024w"
    sizes="100vw">
    srcset="/img/carousel/400/hero1.jpg 400w,
    /img/carousel/800/hero1.jpg 800w,
    /img/carousel/1024/hero1.jpg 1024w"
    sizes="100vw">



    View Slide

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


    data-src="hero2"
    alt="Second slide">



    Index.html ~ line 93, 98 & 103

    View Slide

  56. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 3: Optimising App startup
    Index.html ~ line 48

    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)
    }
    }
    }

    View Slide

  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

    View Slide

  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

    View Slide

  59. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Lab
    Optimise your images
    Add a picture element
    Add lazy loading

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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'
    ]);
    });
    }

    View Slide

  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)
    }));
    }
    });

    View Slide

  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
    }
    });
    });
    };

    View Slide

  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

    View Slide

  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

    View Slide

  70. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 3: Optimising App startup
    /etc/apache2/sites-available/default-ssl.conf

    Header add Link ”;rel=preload;as=style"
    Header add Link ";rel=preload;as=style"

    View Slide

  71. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Lab
    Add Push hints to Apache Configuration

    View Slide

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

    View Slide

  73. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 3: Optimising App startup
    JavaScript best practice
    • Compress
    • Cache
    • Minify
    Async
    Defer

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  77. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 3: Optimising App startup
    Index.html ~ line 132





    View Slide

  78. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Section 3: Optimising App startup
    Index.html ~ line 132





    View Slide

  79. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Lab
    Experiment adding DEFER / ASYNC attributes to JS on PLP / PDP

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  84. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    lunch

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  107. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Lab
    Create variant images
    Update image source
    Add code to lazy load

    View Slide

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

    View Slide

  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

    View Slide

  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/

    View Slide

  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/

    View Slide

  112. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Lab
    Configure Service Worker to Cache every file accessed

    View Slide

  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

    View Slide

  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

    View Slide

  115. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    coffee

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':<br/>new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],<br/>j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=<br/>'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);<br/>})(window,document,'script','dataLayer','GTM-N5T323S');

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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)]));
    }
    });

    View Slide

  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);
    });
    }

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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!

    View Slide

  135. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Lab
    Add Google Analytics offline support

    View Slide

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

    View Slide

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

    View Slide

  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/

    View Slide

  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…

    View Slide

  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());
    }
    });

    View Slide

  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());
    }
    });

    View Slide

  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);
    }

    View Slide

  143. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Lab
    Add Network Awareness for Preload
    Add LQ Thumbnails for Data Saver

    View Slide

  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());
    }
    });

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  153. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81
    Summary

    View Slide

  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

    View Slide

  155. Gareth Hughes @Brassic_lint | Michael Gooding @Michael_G_81

    View Slide