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

The Hitchhiker's Guide to the Front-End Performance, 2018 edition

97d21da8e0ffa8f81218a293482c253a?s=47 Matheus
September 15, 2018

The Hitchhiker's Guide to the Front-End Performance, 2018 edition

Optimizing applications to offer the best user experience isn't something trivial even for the most skilled front-end developers; given Murphy's law, browsers don't always know what critical resources they should load first, huge code bundles and users' slow network connections make them wait more seconds than they want to and, once the whole download is finished, we know that JavaScript can even take a few more seconds to process.

Thankfully there's a lot we can do to give our users a much better experience: preloading, prefetching, code splitting, common chunks, dynamic imports, service workers, web workers, JavaScript compile-time optimization and so on. Yet, in a world where all we hear is PWA hype driven development ™, how these different pieces of the web interact to bring our users a real meaningful better experience is often not well understood, explained or put in practice.

In this talk I'll explain how to ship way faster front-end bundles and share a few general data-driven techniques and performance patterns learnt during my journey building geodata visualization platforms at scale focused on three aspects: faster loading times, better performance and smarter mobile data usage.

97d21da8e0ffa8f81218a293482c253a?s=128

Matheus

September 15, 2018
Tweet

Transcript

  1. The Hitchhiker's Guide to the Front-End Performance The Hitchhiker's Guide

    to the Front-End Performance 2018 EDITION
  2. Who the hell am I? Who the hell am I?

  3. ˜ @ythecombinator on the webs ˜ Engineering Leader, Front-End @beakyn

    ˜ previously @Apple Developer Academy and unemployed indie developer software consultant ˜ addicted to emojis, memes and beer ˜ this ( ) means #after
  4. ˜ And I’m from… ˜

  5. None
  6. None
  7. None
  8. None
  9. http://time.com/3490758/ hitchhikers-guide-galaxy/

  10. ˜ Sounds good.

  11. https://speakerdeck.com/ ythecombinator/the-hitchhiker-s-guide-to-the-front-end-performance

  12. What do we know about perf? What do we know

    about perf?
  13.  “Feijão com Arroz” seal ˜ GZIP everything ˜ Minify

    both CSS and JS ˜ HTML must be compressed ˜ CSS goes on <head> ˜ JS goes lastly on </body> ˜ Optimize all the images (e.g. svgo)
  14. Issues I was facing Issues I was facing

  15. ˜ What I do at …

  16. ˜ What I do at …

  17. None
  18. None
  19. ˜ Perf ˜

  20. ˜ JavaScript ˜

  21. "Write modern, idiomatic JavaScript, and let the JavaScript engine worry

    about making it fast." - Mathias Bynens, BrazilJS 2018
  22. ˜ JavaScript ˜

  23. Before version 5.9 of V8 came out, the engine used

    two compilers: ˜ Full Codegen A simple and very fast compiler that produced simple and relatively slow machine code ˜ Crankshaft A more complex (Just-In-Time) optimizing compiler that produced highly-optimized code
  24. Optimization Killers

  25. Optimization Killers ~ Generators and async functions ~ for-of and

    destructuring ~ try-catch and try-finally ~ Compound let or const assignment ~ Object literals that contain __proto__, or get or set declarations. ~ debugger or with statements ~ Literal calls to eval() ~ …
  26. Performance cliffs in Crankshaft (function good() {
 const start =

    Date.now();
 for (var i = 0; i < 1e8; i++) {}
 console.log(Date.now() - start);
 })(); ˜80ms (function bad() {
 const start = Date.now();
 for (var i = 0; i < 1e8; i++) {}
 console.log(Date.now() - start);
 const whatever = 1;
 })(); ˜230ms
  27. Performance cliffs in Crankshaft (function good() {
 const start =

    Date.now();
 for (var i = 0; i < 1e8; i++) {}
 console.log(Date.now() - start);
 })(); (function bad() {
 const start = Date.now();
 for (var i = 0; i < 1e8; i++) {}
 console.log(Date.now() - start);
 const whatever = 1;
 })(); ˜3x slowdown with Crankshaft
  28. None
  29. The new execution pipeline is built on top of Ignition,

    V8’s interpreter, and TurboFan, V8’s newest optimizing compiler.
  30. Optimization Killers ~ Generators and async functions ~ for-of and

    destructuring ~ try-catch and try-finally ~ Compound let or const assignment ~ Object literals that contain __proto__, or get or set declarations. ~ debugger or with statements ~ Literal calls to eval() ~ …
  31. 60% faster web developer tools http://v8project.blogspot.com/2017/11/web-tooling-benchmark.html

  32. https://twitter.com/popeindustries/status/895575424406867968

  33. "Write modern, idiomatic JavaScript, and let the JavaScript engine worry

    about making it fast." - Mathias Bynens, BrazilJS 2018
  34. None
  35. ˜ #1 ˜ Arrays

  36. ˜ What can %& do… ˜ Avoid pre-allocating large arrays

    ˜ Don’t delete elements in arrays ˜ What it does… Sparse arrays which don’t have every element inside them are a hash table. Elements in such arrays are more expensive to access.
  37. ˜ #2 ˜ Properties

  38. ˜ What can %& do… ˜ Always instantiate your object

    properties in the same order ˜ Assign all of an object’s properties in its constructor
  39. function Point(x, y) { this.x = x; this.y = y;

    } var p1 = new Point(1, 2);
  40. function Point(x, y) { this.x = x; this.y = y;

    } var p1 = new Point(1, 2); Point C0
  41. function Point(x, y) { this.x = x; this.y = y;

    } var p1 = new Point(1, 2); Point C1 For x, see offset 0 C0 If x is added, transition to C1
  42. Point C2 function Point(x, y) { this.x = x; this.y

    = y; } var p1 = new Point(1, 2); C1 For x, see offset 0 If y is added, transition to C2 For x, see offset 0 For y, see offset 1 C0 If x is added, transition to C1
  43. function Point(x, y) { this.x = x; this.y = y;

    } var p1 = new Point(1, 2); p1.a = 5; p1.b = 6; var p2 = new Point(3, 4); p2.b = 7; p2.a = 8;
  44. function Point(x, y) { this.x = x; this.y = y;

    } var p1 = new Point(1, 2); p1.a = 5; p1.b = 6; var p2 = new Point(3, 4); p2.b = 7; p2.a = 8;
  45. function Point(x, y) { this.x = x; this.y = y;

    } var p1 = new Point(1, 2); p1.a = 5; p1.b = 6; var p2 = new Point(3, 4); p2.b = 7; p2.a = 8;
  46. ˜ Always instantiate your object properties in the same order

    ˜ Assign all of an object’s properties in its constructor
  47. None
  48. ˜ fixing perf issues be like ˜

  49. ˜ fixing perf issues be like ˜

  50. ˜ Problems ˜

  51. ˜ #1 ˜ Critical Resources Late Delivered

  52. ˜ Connecting to critical origins (preconnect) ˜ Asset for current

    page (preload) ˜ Asset for future navigation (prefetch)
  53. ˜ Connecting to critical origins (preconnect) ˜ Asset for current

    page (preload) ˜ Asset for future navigation (prefetch)
  54. <link rel="preconnect" href="https://fonts.googleapis.com/" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin> ˜ What

    says… ˜ What can %& do…
  55. ˜ What it does… Preconnect allows the browser to setup

    early connections before an HTTP request is actually sent to the server. This includes: ˜ DNS lookups ˜ TLS negotiations ˜ TCP handshakes This in turn: ✔ Eliminates roundtrip latency ✔ Saves time for users
  56. ˜ Bring me the ! 100MS 200MS 300MS 400MS 500MS

    600MS 700MS HTML CSS FONT 1 FONT 2 Fonts start loading Fonts rendered
  57. ˜ Bring me the ! 100MS 200MS 300MS 400MS 500MS

    600MS 700MS HTML CSS FONT 1 FONT 2 Fonts start loading Fonts rendered FONT 1 FONT 2
  58. ˜ Can I use?

  59. ˜ Connecting to critical origins (preconnect) ˜ Asset for current

    page (preload) ˜ Asset for future navigation (prefetch)
  60. <link rel="preload" href="https://example.com/fonts/font.woff" as="font" crossorigin> ˜ What says… ˜ What

    can %& do…
  61. ˜ What it does… Some of the benefits of the

    preload directive include: ✔ Gives the browser the ability to determine the resource type (it can tell if the same resource can be reused in the future) ✔ The browser can determine if the request is compliant with the content security policy ✔ The browser can send the appropriate accept headers based on resource type
  62. ˜ Can I use?

  63. ˜ Connecting to critical origins (preconnect) ˜ Asset for current

    page (preload) ˜ Asset for future navigation (prefetch)
  64. ˜ What it does… ✔ Fetch resources in the background

    (idle time) ✔ Store them in the browser’s cache It can be:
  65. Link Prefetching DNS Prefetching Prerendering

  66. Link Prefetching DNS Prefetching Prerendering ˜ What it does… Allows

    the browser to fetch resources, store them in cache, assuming that the user will request them. <link rel="prefetch" href="/uploads/images/pic.png"> ˜ What can %& do…
  67. Link Prefetching DNS Prefetching Prerendering ˜ What it does… Allows

    the browser to perform DNS lookups on a page in the background while the user is browsing. <link rel=“dns-prefetch” href="#$fonts.googleapis.com"> ˜ What can %& do…
  68. Link Prefetching DNS Prefetching Prerendering ˜ What it does… Allows

    the browser to render the entire page in the background, all the assets of a document. <link rel=“prerender” href=“https://xandaviao.com.br/”> ˜ What can %& do…
  69. Link Prefetching DNS Prefetching Prerendering ˜ What it does… Allows

    the browser to render the entire page in the background, all the assets of a document. <link rel=“prerender” href=“https://xandaviao.com.br/”> ˜ What can %& do… ⚠ ❌ ❌ ⚠
  70. Link Prefetching DNS Prefetching Prerendering ˜ What it does… Allows

    the browser to render the entire page in the background, all the assets of a document. <link rel=“prerender” href=“https://xandaviao.com.br/”> ˜ What can %& do… ✘ You want to be more careful with prerendering as it is resource heavy and can cause bandwidth waste, especially on mobile devices
  71. ˜ Can I use?

  72. ˜ Can I use?

  73. ˜ Can I use?

  74. https://wicg.github.io/priority-hints/

  75. %%%& A script is to be loaded with critical importance

    as it is necessary for the core user experience ''( <script src=foo importance=critical> %%%& An image is to be loaded with high importance. It could be important but not critical to the overall experience loading up. ''( <img src=foo importance=high> %%%& It can be used to indicate low importance/non-blocking style which isn't impacting the core experience. ''( <link rel=stylesheet href=foo importance=low>
  76. #$ First import(/* webpackPrefetch: 3 */ "assets/images/foo.jpg"); #$ Second import(/*

    webpackPrefetch: 2 */ "modules/foo"); #$ Last import(/* webpackPrefetch: 1 */ “modules/bar");
  77. Preloaded Chunk Prefetched Chunk X

  78. Preloaded Chunk Prefetched Chunk X ˜ Starts loading in parallel

    to the parent chunk ˜ Focuses on current navigation ˜ Fetches resources with high-priority ˜ Is instantly downloaded ˜ Starts after the parent chunk finish ˜ Focuses on fetching resources for the next navigation ˜ Fetches resources with low priority ˜ Is downloaded in browser idle time
  79. ˜ Connecting to critical origins (preconnect) ˜ Asset for current

    page (preload) ˜ Asset for future navigation (prefetch)
  80. ˜ #2 ˜ Blocked First Paint

  81. ˜ Avoid invisible text while web fonts are loading ˜

    Reduce render blocking scripts and stylesheets ˜ Server-side render stuff
  82. ˜ Avoid invisible text while web fonts are loading ˜

    Reduce render blocking scripts and stylesheets ˜ Server-side render stuff
  83. ˜ What says… ˜ What can %& do… @font-face {

    font-family: "Open Sans Regular"; src: url(“./OpenSans-Regular-BasicLatin.woff2") format("woff2"); font-display: swap; }
  84. ˜ What says… ˜ What can %& do… @font-face {

    font-family: "Open Sans Regular"; src: url(“./OpenSans-Regular-BasicLatin.woff2") format("woff2"); font-display: swap; } ˜ What it does… The initial font displayed is the first system font in the stack. When the custom font has loaded, it will kick in and replace the system font that was initially displayed.
  85. ˜ Avoid invisible text while web fonts are loading ˜

    Reduce render blocking scripts and stylesheets ˜ Server-side render stuff
  86. ˜ What says… ˜ What can %& do… https://github.com/addyosmani/critical

  87. ˜ Avoid invisible text while web fonts are loading ˜

    Reduce render blocking scripts and stylesheets ˜ Server-side render stuff
  88. https://rauchg.com/2014/7-principles-of-rich-web-applications

  89. https://nextjs.org

  90. ˜ Avoid invisible text while web fonts are loading ˜

    Reduce render blocking scripts and stylesheets ˜ Server-side render stuff
  91. ˜ #3 ˜ Bloated Pages

  92. ˜ Audit your assets regularly ˜ Code Split (Routes, Components,

    Vendor bundles) + Dynamic Import stuff ˜ Remove unused library code + Tree Shake
  93. ˜ Audit your assets regularly ˜ Code Split (Routes, Components,

    Vendor bundles) + Dynamic Import stuff ˜ Remove unused library code + Tree Shake
  94. Bundlephobia Import Cost for VSCode Webpack Bundle Analyzer

  95. ˜ Audit your assets regularly ˜ Code Split (Routes, Components,

    Vendor bundles) + Dynamic Import stuff ˜ Remove unused library code + Tree Shake
  96. 1.8s bootup 0.78s bootup ˜ What says… ˜ What can

    %& do…
  97. 1.8s bootup 0.78s bootup ˜ What says… ˜ What can

    %& do… 56% faster
  98. ˜ Audit your assets regularly ˜ Code Split (Routes, Components,

    Vendor bundles) + Dynamic Import stuff ˜ Remove unused library code + Tree Shake
  99. https://github.com/GoogleChromeLabs/webpack-libs-optimizations

  100. None
  101. Webpack is still spanked by tree shaking (and from 4

    on) If using TS, target ES6
  102. ˜ Audit your assets regularly ˜ Code Split (Routes, Components,

    Vendor bundles) + Dynamic Import stuff ˜ Remove unused library code + Tree Shake
  103. ˜ #4 ˜ Unnecessary Resources

  104. ˜ Automate minification (UglifyJS, Cloudfare, mod_pagespeed) ˜ Compress (gzip, brotli)

  105. ˜ Automate minification (UglifyJS, Cloudfare, mod_pagespeed) ˜ Compress (gzip, brotli)

  106. https://www.modpagespeed.com/

  107. ✔ Static sites ✔ In control of your Apache or

    Nginx servers ✔ No budget for this ✘ Dynamic content / Rapidly changing websites
  108. Among the 40+ optimisation filters available in mod_pagespeed are: ✔

    optimization, compression and resizing of images; ✔ concatenation, minification, and inlining of your CSS and JavaScript; ✔ cache extension, domain sharding, and domain rewriting; ✔ deferred loading of JavaScript and image resources
  109. ˜ Automate minification (UglifyJS, Cloudfare, mod_pagespeed) ˜ Compress (gzip, brotli)

  110. ˜ -30%

  111. None
  112. None
  113. ⚠ ❌ ❌ ⚠

  114. ˜ Automate minification (UglifyJS, Cloudfare, mod_pagespeed) ˜ Compress (gzip, brotli)

  115. ˜ #5 ˜ Unused Code

  116. ˜ Code coverage in DevTools (Page load, runtime) ˜ Lighthouse

    Coverage Audit ˜ Remove unused code to improve load time ˜ Write regression tests
  117. ˜ Code coverage in DevTools (Page load, runtime) ˜ Lighthouse

    Coverage Audit ˜ Remove unused code to improve load time ˜ Write regression tests
  118. 95% unused

  119. ˜ Code coverage in DevTools (Page load, runtime) ˜ Lighthouse

    Coverage Audit ˜ Remove unused code to improve load time ˜ Write regression tests
  120. https://github.com/purifycss/purifycss

  121. ˜ Code coverage in DevTools (Page load, runtime) ˜ Lighthouse

    Coverage Audit ˜ Remove unused code to improve load time ˜ Write regression tests
  122. (+ jest-image-snapshot)

  123. ˜ Code coverage in DevTools (Page load, runtime) ˜ Lighthouse

    Coverage Audit ˜ Remove unused code to improve load time ˜ Write regression tests
  124. ˜ #6 ˜ Unoptimized Images

  125. ˜ Optimize images ˜ Use lighter formats ˜ Use responsive

    images ˜ Lazy-load offscreen images
  126. ˜ Optimize images ˜ Use lighter formats ˜ Use responsive

    images ˜ Lazy-load offscreen images
  127. ˜ What says…

  128. ˜ GUI ˜ ImageOptmin (Mac) XNConvert (Cross-platform) ˜ Build ˜

    imagemin libvips ˜ CDN ˜ Cloudinary Imgix Fastly Akamai ˜ CDN ˜ (self-hosted) Thumbor ImageFlow ˜ What can %& do…
  129. ˜ GUI ˜ ImageOptmin (Mac) XNConvert (Cross-platform) ˜ Build ˜

    imagemin libvips ˜ CDN ˜ Cloudinary Imgix Fastly Akamai ˜ CDN ˜ (self-hosted) Thumbor ImageFlow
  130. ˜ What it does…

  131. ˜ Optimize images ˜ Use lighter formats ˜ Use responsive

    images ˜ Lazy-load offscreen images
  132. <img src="banda_magnificos.gif"> 7.3mb <video autoplay muted playinline> <source src="banda_magnificos.mp4" type="video/mp4">

    </video> 1.3MB <video autoplay muted playinline> <source src=“banda_magnificos.webm” type=“video/webm"> </video> 960kb 80%+ savings
  133. ˜ Optimize images ˜ Use lighter formats ˜ Use responsive

    images ˜ Lazy-load offscreen images
  134. https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType

  135. https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType

  136. https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType

  137. None
  138. 4G+ (Video) ˜1mb 2G-3G (Static Image) ˜30kb

  139. ˜ Optimize images ˜ Use lighter formats ˜ Use responsive

    images ˜ Lazy-load offscreen images
  140. https://github.com/aFarkas/lazysizes

  141. ˜ Optimize images ˜ Use lighter formats ˜ Use responsive

    images ˜ Lazy-load offscreen images
  142. ˜ #7 ˜ Wasted Renders

  143. None
  144. ˜ Use referentially equal stuff ˜ Use PureComponent/memo (when it

    makes sense) ˜ Take benefit from techniques like windowing
  145. "In God we trust; all others must bring data." -

    W. Edwards Deming
  146. Things I’d fucking love to experiment with Things I’d fucking

    love to experiment with
  147. ˜ #1 ˜

  148. https://github.com/developit/stockroom

  149. ˜ #2 ˜

  150. None
  151. ˜ Progressive Web Apps (PWAs) ˜ Devops/Something-as-a-Service ˜ Blockchain ˜

    Machine Learning/Deep Learning/ Neural Networks (& other Artificial Intelligence slangs I’ve got no idea of)
  152. ˜ Progressive Web Apps (PWAs) ˜ Devops/Something-as-a-Service ˜ Blockchain ˜

    Machine Learning/Deep Learning/ Neural Networks (& other Artificial Intelligence slangs I’ve got no idea of)
  153. None
  154. #$ Require GuessPlugin const {GuessPlugin} = require('guess-webpack'); #$ Add it

    in the end of your webpack #$ plugin configuration GuessPlugin({ GA: 'GA_VIEW_ID' })
  155. ˜ GA Module fetches structured data from Google Analytics ˜

    Guess Parser parses an application in order to create the mapping between routes and JavaScript bundles ˜ Guess Webpack Plugin automates the process of applying data-driven bundling & pre-fetching in React & Angular apps
  156. Its goal is to replace the manual decision making with

    an automated data-driven approach. Focusing on: ˜ Single-page applications ˜ Static content sites ˜ Framework-based static sites
  157. None
  158. https://github.com/addyosmani/predictive-fetching

  159. https://bit.ly/2u2fUzW

  160. https://www.youtube.com/watch?v=L5tPWCB7jX0

  161. None
  162. Thanks Thanks