Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

High Performance PWAs

51 views

Published on

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.

Published in: Technology
  • Be the first to comment

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

×