Successfully reported this slideshow.
Your SlideShare is downloading. ×

High Performance PWAs

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 163 Ad

High Performance PWAs

Download to read offline

Your users deserve a fast and responsive web app and PWAs help you step that up a notch through notifications, offline support and more.

There’s a lot that goes into that from understanding how the DOM tree works and how that plays with CSS and JavaScript to how to leverage the ServiceWorker for cashing and push notifications.

In this session, we’ll build a PWA that show cases many of the things you need to keep in mind when building a great and fast progressive web app.

Your users deserve a fast and responsive web app and PWAs help you step that up a notch through notifications, offline support and more.

There’s a lot that goes into that from understanding how the DOM tree works and how that plays with CSS and JavaScript to how to leverage the ServiceWorker for cashing and push notifications.

In this session, we’ll build a PWA that show cases many of the things you need to keep in mind when building a great and fast progressive web app.

Advertisement
Advertisement

More Related Content

Recently uploaded (20)

Advertisement

High Performance PWAs

  1. 1. @joshholmes Performant PWAs Josh Holmes josh.holmes@microsoft.com
  2. 2. @joshholmes
  3. 3. @joshholmes Microsoft Edge and Chromium Open Source: Our Intent 1. We will adopt Chromium as the web platform for Microsoft Edge desktop. 2. We will evolve the Microsoft Edge app architecture, enabling distribution to all supported versions of Windows including Windows 7 and Windows 8, as well as Windows 10. We will also bring Microsoft Edge to other desktop platforms, such as macOS. 3. We will offer our Windows platform expertise to improve the experience of all Chromium-based browsers on Windows. https://github.com/MicrosoftEdge/MSEdge
  4. 4. @joshholmes A.K.A. “PWA”
  5. 5. @joshholmes PROGRESSIVE WEB APP
  6. 6. @joshholmes Game Gallery Book Newspaper Art Project Tool PROGRESSIVE WEB APP
  7. 7. @joshholmes PROGRESSIVE WEB SITE
  8. 8. @joshholmes PROGRESSIVE WEB SITE++
  9. 9. @joshholmes PROGRESSIVE WEB SITE++ HTTPS Manifest.json ServiceWorker.js
  10. 10. @joshholmes PWA IS
  11. 11. @joshholmes PWA IS
  12. 12. @joshholmes PWA IS
  13. 13. @joshholmes PWA IS
  14. 14. @joshholmes PWA IS
  15. 15. @joshholmes PWA IS
  16. 16. @joshholmes PWA IS
  17. 17. @joshholmes PWA IS
  18. 18. @joshholmes PWA IS
  19. 19. @joshholmes PWA IS
  20. 20. @joshholmes PWA IS
  21. 21. @joshholmes https://www.pwabuilder.com/
  22. 22. @joshholmes {"dir" : "ltr", "lang" : "en", "name" : "Twitter", "scope" : "/", "display" : "standalone", "start_url" : "https://m.twitter.com/", "short_name" : "Twitter", "theme_color" : "#ffffff", "description" : "", "orientation" : "any", "background_color" : "transparent", "related_applications" : "", "prefer_related_applications" : "false", "icons" : [ { "src": "https://abs.twimg.com/responsive- web/web/icon-ios.8ea219d08eafdfa4.png", "sizes": "192x192" } ]} Manifest.json
  23. 23. @joshholmes //Install stage sets up the offline page in the cache and opens a new cache self.addEventListener('install', function(event) { var offlinePage = new Request('offline.html'); event.waitUntil( fetch(offlinePage).then(function(response) { return caches.open('pwabuilder-offline').then(function(cache) { console.log('[PWA Builder] Cached offline page during Install'+ response.url); return cache.put(offlinePage, response); }); })); }); //If any fetch fails, it will show the offline page. self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request).catch(function(error) { console.error( '[PWA Builder] Network request Failed. Serving offline page ' + error ); return caches.open('pwabuilder-offline').then(function(cache) { return cache.match('offline.html'); }); } )); }); //This is a event that can be fired from your page to tell the SW to update the offline page self.addEventListener('refreshOffline', function(response) { return caches.open('pwabuilder-offline').then(function(cache) { console.log('[PWA Builder] Offline page updated from refreshOffline event: '+ response.url); return cache.put(offlinePage, response); }); }); Service Worker
  24. 24. @joshholmes //Add this below content to your HTML page, or add the js file to your page at the very top to register service worker if (navigator.serviceWorker.controller) { console.log('[PWA Builder] active service worker found, no need to register') } else { //Register the ServiceWorker navigator.serviceWorker.register('pwabuider-sw.js', { scope: './' }).then(function(reg) { console.log('Service worker has been registered for scope:'+ reg.scope); }); } Register the SW
  25. 25. @joshholmes /ˈfrikSH(ə)n/
  26. 26. @joshholmes Poor performance is friction
  27. 27. @joshholmes Source: eMarketer
  28. 28. @joshholmes Source: eMarketer Which of these matter to your context… Always measure what matters…
  29. 29. @joshholmes A 1s delay in page load can reduce conversions by 7% Source: Kissmetrics
  30. 30. @joshholmes For an online shop earning $100k/day, that’s about $2.5m in lost sales Source: Kissmetrics
  31. 31. @joshholmes For Amazon, 1s is worth about $1.6b in sales Source: HubSpot
  32. 32. @joshholmes Source: eMarketer
  33. 33. @joshholmes 53% abandon websites that take more than 3s to load Source: Google
  34. 34. @joshholmes By shaving 7s off load, Edmunds increased page views by 17% & ad revenue by 3% Source: HubSpot
  35. 35. @joshholmes Mozilla reduced page load by 2.2s and saw a 15.4% increase in downloads Source: HubSpot
  36. 36. @joshholmes Performance matters
  37. 37. @joshholmes /dôɡˈfo͞odiNG/
  38. 38. @joshholmes Let’s talk (briefly) about page load
  39. 39. @joshholmes time Your Device The Web DNS Lookup Icons by Mahmure Alp
  40. 40. @joshholmes time Your Device The Web TCP Handshake Icons by Mahmure Alp
  41. 41. @joshholmes time Your Device The Web Request Icons by Mahmure Alp
  42. 42. @joshholmes time Your Device The Web Server Processing Icons by Mahmure Alp
  43. 43. @joshholmes time Your Device The Web Response Icons by Mahmure Alp
  44. 44. @joshholmes Icons by Mahmure Alp
  45. 45. @joshholmes WEB RUNTIME ARCHITECTURE
  46. 46. @joshholmes WEB RUNTIME ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  47. 47. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  48. 48. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  49. 49. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  50. 50. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  51. 51. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  52. 52. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  53. 53. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  54. 54. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  55. 55. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  56. 56. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  57. 57. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  58. 58. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  59. 59. @joshholmes WEB PLATFORM ARCHITECTURE Networking / Cache Parsers 1 2 7 43 8 9 5 6 DOM Tree Formatting Layout Painting 1 2 7 43 8 9 5 6 Display Tree Compositing DOM API & Capabilities JavaScript DMANIP Hit Testing InputCSS Cascade
  60. 60. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  61. 61. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  62. 62. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  63. 63. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  64. 64. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  65. 65. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  66. 66. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  67. 67. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  68. 68. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  69. 69. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  70. 70. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  71. 71. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  72. 72. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  73. 73. @joshholmes DOM Parsing <html> <head> <title>Silly example</title> <script src="head.js"></script> <link rel="stylesheet" href="main.css"> <style>h2 { font-weight: bold; }</style> <script>console.log('hi');</script> </head> <body> <img src="my.png" alt=""> <script src="body.js"></script> </body> </html>
  74. 74. @joshholmes Steps for better performance 1. Use native features whenever possible 2. Only include assets you actually need 3. Optimize everything 4. Think about when you load assets 5. Consider how you load assets 6. Only load assets when they add value 75
  75. 75. @joshholmes Step 1: Use native features whenever possible
  76. 76. @joshholmes (they’re effectively free)
  77. 77. @joshholmes Por exemplo <header> Header content… </header>
  78. 78. @joshholmes Por exemplo <input id="n" name="n" required aria-required="true" autocorrect="off" autocapitalize="words" placeholder="Sir Tim Berners Lee" autocomplete="name" >
  79. 79. @joshholmes Por exemplo <input type="email" id="e" name="e" required aria-required="true" autocorrect="off" autocapitalize="off" autocomplete="email" placeholder="you@yourdomain.tld" >
  80. 80. @joshholmes Por exemplo @media (min-width:600px) { .gridded { display: grid; grid-template-columns: 1fr 300px; grid-gap: 20px; } }
  81. 81. @joshholmes Step 2: Only include assets you actually need
  82. 82. @joshholmes Great tools, possibly overkill
  83. 83. @joshholmes Every tool has a cost Framework Size (Compressed) Bootstrap 2 149 kB Bootstrap 3 103 kB Angular 1.4 51 kB Ember 2.2.0 111 kB Foundation 4 266 kB jQuery 32 kB UI Kit 86 kB React 16 + React DOM 35 kB Vue 2.4.2 20 kB
  84. 84. @joshholmes Chances are, your library of choice is on a CDN
  85. 85. @joshholmes time Your Device The Web Icons by Mahmure Alp
  86. 86. @joshholmes time Your Device The Web Icons by Mahmure Alp You can optimize this
  87. 87. @joshholmes Find the server early <link rel="prefetch" href="https://cdn.foo.com">
  88. 88. @joshholmes time Your Device The Web Icons by Mahmure Alp You can optimize this
  89. 89. @joshholmes Go for the handshake <link rel="preconnect" href="https://cdn.foo.com">
  90. 90. @joshholmes time Your Device The Web Icons by Mahmure Alp You can even optimize this
  91. 91. @joshholmes Grab that resource <link rel="preload" href="https://cdn.foo.com/jquery.min.js" as="script">
  92. 92. @joshholmes Download isn’t everything Source: The Filament Group
  93. 93. @joshholmes Download isn’t everything Framework Method/function operations/s Vanilla JS document.getElementById() 12,137,211 Dojo dojo.byId(); 5,443,343 Prototype $() 2,940,734 Ext JS Ext.get() 997,562 jQuery $() 350,557 YUI YAHOO.util.Dom.get() 326,534 MooTools document.id() 78,802 Source: VanillaJS
  94. 94. @joshholmes
  95. 95. @joshholmes We used some hints though <link rel="preconnect" href="//10kapart.blob.core.windows.net"> <link rel="preconnect" href="//cdnjs.cloudflare.com"> <link rel="preconnect" href="//www.google-analytics.com">
  96. 96. @joshholmes Step 3: Optimize everything
  97. 97. @joshholmes Our approach to CSS (Gulp) 1. Write modular CSS in Sass (+ Breakup for MQ management) 2. Compile CSS with a precision of 4 decimal places (gulp-sass) 3. Fallbacks for the last 2 browser versions (gulp-autoprefixer) 4. CSS shorthand declarations if possible (gulp-shorthand) 5. Remove any unused declarations/rule sets (gulp-uncss) 6. Optimize the files (gulp-csso) 7. Minify (gulp-clean-css) 8. Gzip (gulp-zopfli) 9. Brotli (gulp-brotli) 103
  98. 98. @joshholmes Before
  99. 99. @joshholmes After
  100. 100. @joshholmes Our approach to JS (Gulp) 1. Write modular JavaScript, grouped as appropriate 2. Combine files based on folder structure (gulp-folders, gulp-concat) 3. Create an wrapping closure to isolate from other JS (gulp-insert) 4. Minify (gulp-uglify) 5. Gzip (gulp-zopfli) 6. Brotli (gulp-brotli) 106
  101. 101. @joshholmes Results about 8 kB all-up
  102. 102. @joshholmes Interesting side note
  103. 103. @joshholmes We also minified & pre-compressed our HTML
  104. 104. @joshholmes Step 4: Think about when you load assets
  105. 105. @joshholmes We had 10 JS files ๏ Global ‣ main.js - the site’s library ‣ serviceworker.js - The site’s service worker ๏ Browser-specific ‣ html5shiv.js - local copy of the HTML5 Shiv for < IE9 111
  106. 106. @joshholmes We had 10 JS files ๏ Page-specific ‣ enter.js - Entry form-related code ‣ form-saver.js - Used to save form entries locally until submitted ‣ hero.js - Runs the SVG animation on the homepage ‣ home.js - Handles homepage-specific tasks ‣ project.js - Used on project pages during voting ‣ update.js - Handles the winner update form 112
  107. 107. @joshholmes Per the common wisdom <script src="/j/main.min.js"></script> </body> </html>
  108. 108. @joshholmes No need to run immediately <script src="/j/main.min.js"></script> <script src="/j/home.min.js" defer ></script> </body> </html>
  109. 109. @joshholmes Run whenever you can <script src="/j/main.min.js"></script> <script src="/j/home.min.js" async ></script> </body> </html>
  110. 110. @joshholmes Consider dependencies <script src="/j/main.min.js"></script> <script src="/j/home.min.js" async></script>
  111. 111. @joshholmes Consider dependencies <script src="/j/main.min.js" async></script> <script src="/j/home.min.js" async></script>
  112. 112. @joshholmes “race condition”
  113. 113. @joshholmes Avoid race conditions <script src="/j/main.min.js"></script> <script src="/j/home.min.js" async></script>
  114. 114. @joshholmes Why so many separate files?
  115. 115. @joshholmes Connections in HTTP/1.1 Browser Per host Overall IE 9 6 35 IE 10 8 17 IE 11 13 17 Firefox 4+ 6 17 Opera 11+ 6 user defined Chrome 4+ 6 10 Safari 7+ 6 17
  116. 116. @joshholmes time Your Device The Web HTTP/1.1 Icons by Mahmure Alp
  117. 117. @joshholmes HTTP/2 creates a single connection and contents stream in
  118. 118. @joshholmes time Your Device The Web HTTP/2 Icons by Mahmure Alp
  119. 119. @joshholmes Source: A List Apart
  120. 120. @joshholmes Demo: Akamai
  121. 121. @joshholmes Source: A List Apart
  122. 122. @joshholmes Step 5: Consider how you load assets
  123. 123. @joshholmes Start simple <link rel="stylesheet" href="/c/d.min.css"> <link rel="stylesheet" href="/c/a.min.css" media="only screen">
  124. 124. @joshholmes Fault tolerance FTW! <link rel="stylesheet" href="/c/d.min.css"> <link rel="stylesheet" href="/c/a.min.css" media="only screen">
  125. 125. @joshholmes Conditional scripting <!--[if lt IE 9]> <script src="/j/html5shiv.min.js"></script> <![endif]-->
  126. 126. @joshholmes Conditional scripting <!--[if gt IE 8]><!--> <script src="/j/main.min.js"></script> <script src="/j/home.min.js" async ></script> <!--<![endif]--> </body> </html>
  127. 127. @joshholmes Conditional imagery
  128. 128. @joshholmes Conditional images @media (min-width: 36.375em) { .presented-by [href*=microsoft]::before { background-image: url(/i/c/edge.png); background-image: url(/i/c/edge.svg), none; … } }
  129. 129. @joshholmes Conditional images @media (min-width: 36.375em) { .presented-by [href*=microsoft]::before { background-image: url(/i/c/edge.png); background-image: url(/i/c/edge.svg), none; … } }
  130. 130. @joshholmes Conditional images @media (min-width: 36.375em) { .presented-by [href*=microsoft]::before { background-image: url(/i/c/edge.png); background-image: url(/i/c/edge.svg), none; … } }
  131. 131. @joshholmes Conditional images @media (min-width: 36.375em) { .presented-by [href*=microsoft]::before { background-image: url(/i/c/edge.png); background-image: url(/i/c/edge.svg), none; … } }
  132. 132. @joshholmes Conditional animation
  133. 133. @joshholmes How do we get there? JS? No No imageLoad Yes <> SVG support? Yes SVG Ajax request SVG Yank out script & add to document No picture Save the markup for next page load NoYes Verify browser width condition
  134. 134. @joshholmes Step 6: Only load assets when they add value
  135. 135. @joshholmes
  136. 136. @joshholmes
  137. 137. @joshholmes Evaluate images case-by-case ๏ Does the image reiterate information found in the surrounding text? ๏ Is the image necessary to understand the surrounding content? ๏ Does the image contain text? ๏ Is the image a graph, chart, or table? ๏ Could the content of the image be presented in a different format that would not require an image? ๏ Is the image purely presentational? 143
  138. 138. @joshholmes 53% of the average web page Source: Internet Archive
  139. 139. @joshholmes And they don’t always fit
  140. 140. @joshholmes Source: The Outline
  141. 141. @joshholmes If you can avoid using an image, do it
  142. 142. @joshholmes If you need an image, choose the best format
  143. 143. @joshholmes Quick format recap ๏ GIF ‣ for images with large swaths of solid colors ‣ Binary transparency ๏ JPG ‣ For photographs and images with gradations of color ‣ Can be compressed (introduces artifacts) 149
  144. 144. @joshholmes Quick format recap ๏ PNG (8-Bit) ‣ Alternative to GIF ‣ Can support alpha transparency (with the right creation software) ๏ PNG (24-bit) ‣ Alternative to JPG ‣ Usually larger than JPGs ‣ Supports alpha tranparency 150
  145. 145. @joshholmes Quick format recap ๏ WebP ‣ Newer format, not universally supported ‣ Smaller than JPGs and 24-bit PNGs ‣ Support alpha transparency ‣ and so much more… 151
  146. 146. @joshholmes Sometimes images are “nice to have”
  147. 147. @joshholmes
  148. 148. @joshholmes
  149. 149. @joshholmes Oh wait… optimize everything
  150. 150. @joshholmes
  151. 151. @joshholmes
  152. 152. @joshholmes Source: 38 kB JPG
  153. 153. @joshholmes B&W: 35 kB JPG (-7%)
  154. 154. @joshholmes Crop & Resize: 12 kB JPG (-68%)
  155. 155. @joshholmes Blur & optimize: 9 kB JPG (-76%)
  156. 156. @joshholmes
  157. 157. @joshholmes WebP: 4 kB (-89%)JPG: 9 kB (-76%)
  158. 158. @joshholmes Steps for better performance 1. Use native features whenever possible 2. Only include assets you actually need 3. Optimize everything 4. Think about when you load assets 5. Consider how you load assets 6. Only load assets when they add value 179
  159. 159. @joshholmes https://webhint.io
  160. 160. @joshholmes Every choice we make affects our users’ experiences
  161. 161. @joshholmes Let’s spend our time to save it for our users
  162. 162. @joshholmes Speedy performance is a great user experience
  163. 163. @joshholmes Performant PWAs Josh Holmes josh.holmes@microsoft.com

Editor's Notes

  • Hi there, my name is Aaron Gustafson
    WaSP
    Microsoft - Web Standards, PWAs & a11y
  • You’ll hear me use the terms Progressive Web App and PWA interchangeably throughout this talk
  • Let’s ignore the first part of this term for a moment, but I promise I will circle back to it shortly.

    The term “web app” may sound like something you can put your finger on, right?
    It’s software, on the Web, you use to complete a task.
    Like the expense manager, but it can be any web site or property, really.
  • And so it is with progressive web apps too.
    “Web apps” in this context can be any website…
    newspapers, games, books, shopping sites…
    it really doesn’t matter what the content or purpose of the website is, the “web app” moniker is applicable to all of them.
  • The term could just have easily been progressive web site and it may be helpful to think of it as such
    It doesn’t need to be a single page app
    You don’t need to be running everything client side
    There are no particular requirements for the type of PWA you are developing
  • Essentially a PWA is a website that is capable of being promoted to being native-ish.
    It gets many of the benefits of being a native app (some of which I will cover in this talk),
    but is also has all of the benefits of being a website too
  • Google defined 10 characteristics they believe define this new breed of web application
  • Progressive: Works for every user, regardless of browser choice because it's built with progressive enhancement as a core tenet
    We’ll circle back to discuss progressive enhancement a bit more in a moment
  • Responsive: Fits any form factor
  • Network independent: Works offline & on low-quality networks (Service Worker)
  • App-like: Feels like an app in terms of responsiveness and UX
  • Fresh: Always up to date (Service Worker)
  • Safe: Served via HTTPS to prevent snooping and to ensure content hasn't been tampered with
  • Discoverable: Search spiders can identify these websites as apps through their use of the Web Application Manifest and Service Worker
  • Re-engageable: Makes re-engagement easy through features like push notifications
  • Installable: Allows users to install apps they find useful, independent of (but not exclusive of) an app store.
  • Linkable: Easily shared via a URL. Can also respond to URLs (deep linking).
  • In terms of designing user experiences, the problem we are often charged with solving is how to help users accomplish a task. In most cases, we want to do as much as we can to remove any obstacles that can get in their way. You know, friction.

    This is probably the 48th time you’ve heard that word at this conference and there’s a reason for that: Our job is to reduce friction in order to help people do what they need to do with as little pain or annoyance as possible.
  • We need to be concerned about the performance of our web pages because poor performance is friction.
  • I’m sure you’ve seen countless studies that have shown time & time again that people want speedy access to websites.

    It matters.

    The Harris Poll found that speed was the second most important attribute of a website, ranked right behind easy navigation, and just ahead of reliability. In other words, they’d rather it be fast than work all the time :-)
  • I’m sure you’ve seen countless studies that have shown time & time again that people want speedy access to websites.

    It matters.

    The Harris Poll found that speed was the second most important attribute of a website, ranked right behind easy navigation, and just ahead of reliability. In other words, they’d rather it be fast than work all the time :-)
  • Going on Akamai and Gomez data, Kissmetrics determined that…
  • Translated…

    Of course this is somewhat hypothetical. Let’s look at practical.
  • Amazon knows how much poor performance costs.
  • Mobiquity found that slow mobile load times was the greatest source of frustration for mobile shoppers.
  • Google has found that…
  • Switching gears from negative to positive…
  • It should be obvious by now that performance really matters.
  • In building the site, I decided to challenge the team to make the contest site abide by the same rules. After all, if we were going to ask it of others, we didn’t want to be hypocrites.

    In a way, we decided to eat our own dogfood.
  • Before I get into the ins and outs of some of the performance optimizations we made to the site, I want to give you a quick overview of how pages load. Just so we are all on the same page.

    This isn’t going to be exhaustive, but it will give you a sense of what we are dealing with when we deliver things on the web.
  • First step when you enter an address in the URL bar

    This may be cached locally or somewhere in your local or provider’s network
  • Then the browser & server greet each other
  • Then the browser requests a file
  • Server processing can be as simple as finding a static file or retrieving a ton of data from a database and crunching a bunch of numbers. This piece is widely variable, but can be tuned as well.

    That discussion falls outside of the boundaries of this talk though.
  • Once we have the response, the browser can get to work doing something with it.
  • This is a really high-level overview of how parsing occurs. Generally this is how it goes, but there are some optimizations individual browsers make that are unique to them.

    This is a really contrived example, but I wanted to use it to inform you of how all of this comes together so you have a better sense of how markup and source order affects page load performance.
  • As the DOM parser moves through the HTML document…
  • Additionally, each request may also require building up a full DNS & TCP handshake request
  • With all of this in mind, here are my recommended first steps for performance tuning your projects…
  • For more on the autocomplete property, Jason Grigsby wrote a very exhaustive walkthrough of what it is and how to use it on the Cloud Four blog.
  • Here’s another example with an email field
  • Modern approaches to layout are far more code-efficient.
  • Modern JavaScript can also allow you to work without libraries.
  • Make use of system fonts. You can even create elaborate font stacks offering a bunch of options that should produce a desirable match.

    This was our setup in the contest site.
  • And the fallbacks were relatively close or at least close enough to not present weird layout issues when our top picks weren’t available.
  • Subsetting means removing unused characters from the font file.

    There are numerous tools for this: command line, Font Squirrel and it’s supported by services like Typekit, Google Fonts
  • But be cautious in how you do this as you might discover you’ve removed a character you actually needed.
  • There are a lot of great tools out there… many may be more than you need.
  • If you use Foundation all-up, that’s about 266 kB or a relatively large image worth of code and you’re only likely to be using a portion of it.

    Some of these libraries and frameworks do enable you to customize a “build” to your needs, only including what you are actually using.

    A colleague of mine was hired to consult on the performance of a high profile retail website. When he tucked into the code, he saw they were using jQuery, Prototype, Scriptaculous, MooTools, and a handful of other libraries. In total, they were loading 3.5 MB of JavaScript. All because their developers didn’t standardize on a single library and just added them willy nilly, as if they were free.
  • Some folks rely on a Content Delivery Network (or CDN) to deliver the libraries they use. That can be great (as long as there’s not a network issue).

    But it’s important to remember, as I mentioned before, that these can incur some additional overhead in the network connection realm. And that can cost precious time, especially on high-latency mobile networks.
  • For each CDN domain, we might have to go through the whole connection process to request a file. The more CDN shards, the more connection overhead you have.
  • Thankfully we can optimize some of these steps a little bit
  • Resource hints
  • Browsers use the “as” attribute to help them slot the request into the right part of the loading process.
  • Of course download isn’t all we need to worry about…

    A few years ago Filament Group ran a test, building the same “app” using several frameworks commonly employed at the time and tested how long it took them to become usable.

    Ember 1.9 was the worst offender, taking nearly 5 seconds to become usable on a mobile device on a 3G network. The reason is a combination of poor network performance with a slower mobile chipset.
  • And even once you have the library downloaded, they usually pale in comparison to JavaScript code when it comes to execution speed.

    Just as a quick example, an ID lookup in jQuery runs about 98% slower than the native DOM method.

    Every layer of abstraction reduce performance.
  • And so, we opted to skip the libraries and frameworks and write pretty much everything from scratch.
  • We did use some hints though.

    And we did borrow the SVG support test from Modernizr.
    (Credited in the source, of course)
  • We used Gulp to help out with a lot of the optimization work.

    Gulp is great, but it’s not the only game in town for this sort of thing. There are other task runners like Grunt or even Make, and bundling systems like Webpack or Parcel. I’m not here to tell you what to use, you should figure out what’s right for you & your team.

    It’s important to point out that we pre-compressed our static assets because that meant there was one less thing for the server to have to do.
  • You can create a map to load files in a particular way, but you can also use filenames in many systems (because they will bundle them alphabetically).

    When you need certain bits of code to be placed early or late in a file. _ & omega can enable that.
  • Even though we were using a Node backend, we pre-compressed our HTML files so we had one less task for the server to do dynamically (further reducing it’s processing time).

    It’s worth noting we also used Varnish to cache dynamic pages generated out of the Node backend.
  • 2 global
    1 browser specific (which I’ll get to)
  • 7 page or feature-specific
  • As you’d expect…
  • And since JS was not necessary, but rather enhanced the page functionality, we were able to defer loading page-specific scripts.
  • Defer is one option, another is async, which means…
  • It’s tempting to defer or asynchronously load every bit of JS, but that can be problematic.
  • What if one file loads before the other. With async we don’t control when things load.
  • We only set async and defer on files other than our main.js file.
  • Parallel connections per host and overall are limited
  • Under HTTP/1.1, we also had to deal with head of line blocking where resource requests need to be resolved before a new one can be made.
  • HTTP/2 is quite an advancement and, among its features, offers…
  • Request as much as you want - 1 connection
  • To throw that into the waterfall chart you might be used to seeing in devtools…
  • Demo Akamai put together that shows HTTP1 vs HTTP2

    379 individual tiles loading in
  • Recommended reading…
  • the default CSS file is tiny
  • In most cases, the older browsers we’re talking about are mobile anyway. Or IE8 and below. And I’m ok giving them a mobile layout to be honest.
  • Conditional comments, while no longer supported in modern versions of IE and Edge can still be useful.

    This one delivers the HTML5 shim to IE8 and below so our HTML5 elements render properly.
  • You can also use them to hide specific content from older browsers. We used them to hide scripts from IE8 and below

    And since the site works without JS, we didn’t have to worry about it.
  • You can also load images conditionally. We did that in the footer with the Microsoft Edge and AEA logos.
  • Uses the hero.js I want to use an Interface Experience Map to show how we enhance for different experiences
  • Images can be useful to draw users’ eyes when dealing with a page where competition is high.
  • They become less useful when competition is low
  • Some images are nice to have, but not necessary.
  • We need to justify each and every one we use.
  • The truth is…

    I highly recommend reading this article on the Outline. It’s interesting, especially as it comes from a non-tech perspective.

    Distillation: If you’re writing an article about cyber security, I really don’t need to see some stock photo of a phony hacker. Unless it adds real value, leave it out.
  • We need to justify each and every image we use.
  • I’m just going to run through some high-level bits about image formats.
    Una (You-nah) Kravets has a great talk on image formats she gave at AEA in Denver last year that goes into way more depth…
  • I’m ok just showing their names as the default experience.
  • We had it both ways. No image as the default and an image if certain conditions were met

    To do it, I used a data attribute and wrote up a little shorthand for defining what to do with the images.
  • Don’t worry about totally following this JS code, but here’s how I got CSS & JS in sync.

    Created a div to enable me to watch for specific breakpoints and injected it dynamically via JavaScript (invisibly of course).
  • In CSS I used font-family to store the breakpoint name for each of the major breakpoints in the design

    Adam Bradley came up with this approach.
  • Then I gave myself a method to access that information from JavaScript.

    This is a simplification of the actual code.
  • Then I could use it.
  • Let’s look at how we got those file sizes so low!
  • Here’s the process we used for each image.
  • First we removed the color information, which saved us about 7% off of the original file size.
  • Then we cropped and resized the images, reducing the file size by 68%
  • Save For Web in Photoshop + ImageOptim
  • Instead of lazy loading an img element, we lazy load a picture element.
  • You can add as many sources as you want, just put the smallest one first as the first match wins.
  • So instead of lazy loading this…
  • We lazy load this
  • To summarize what I just covered.
  • It’s important to recognize that…

    We must…

    Think about what we load & how we load it
    Be realistic about what we ”need”
    Make every resource have to fight for its place in our sites
    Realize that there are trade-offs between design and performance
  • We should always aim to make decisions in our users’ favor over our own desires
  • After all…
  • Hi there, my name is Aaron Gustafson
    WaSP
    Microsoft - Web Standards, PWAs & a11y

×