Lessons Learned Sciencing The Web

96270e4c3e5e9806cf7245475c00b275?s=47 Addy Osmani
November 11, 2017

Lessons Learned Sciencing The Web

My talk from FFConf 2017.

96270e4c3e5e9806cf7245475c00b275?s=128

Addy Osmani

November 11, 2017
Tweet

Transcript

  1. @addyosmani

  2. @addyosmani

  3. LOAD ONLY WHAT YOU NEED WHEN YOU NEED IT n

  4. DON’T BE BIG ✂ Code-split your JavaScript Compress resources ⚡

    Minify & optimize *.* Tree-shake modules Respect data plans Don’t over-do Web Fonts ONLY LOAD WHAT YOU NEED Lazy-load non-critical resources Preconnect to important origins Preload critical resources Minimize redirects & round-trips ONLY LOAD WHAT CHANGED Cache resources effectively Be network resilient with Service Workers LOADING BEST PRACTICES
  5. User Expectations

  6. None
  7. RESPONSE ANIMATION IDLE LOAD RAIL Evolving

  8. First Paint First Meaningful Paint Time To Interactive User happiness

    metrics First Contentful Paint
  9. Time to Interactive <5s on an average mobile device over

    3G *2s on repeat-load a:er Service Worker registered goal
  10. Latencies are significantly higher than a wired connection

  11. None
  12. Can we data-science the web?

  13. None
  14. None
  15. None
  16. THIS CAN’T GO WRONG.

  17. NEW GAME OPTIONS A ROAD RASH RIFF

  18. PLAYER 1 REMY LEVEL 1 A TOTALLY SAFE ROAD

  19. STAGE 1: MEASURE REMY REMY

  20. Chrome DevTools Lighthouse WebPageTest TOOLS TO SCIENCE THE WEB Synthetic

    lab conditions Real-world RUM Synthetic health of the web Puppeteer ~500K sites BETA
  21. Queryable RUM for the web?

  22. bit.ly/introducing-crux

  23. Origin Form Factor Effective Connection Type (e.g 3G, 4G) First

    Paint First ContentFul Paint domContentLoaded onLoad
  24. // Network type that browser uses
 navigator.connection.type > 'wifi' 


    // New: Effective connection type // using rtt and downlink values
 navigator.connection.effectiveType > '2G' I want to adapt serving based on estimated network quality BEFORE AFTER For more on navigator.connection.* See ‘Building a modern media experience’ Chrome 62
  25. RUM Chrome UX Report

  26. STAGE 2: OPTIMIZE REMY

  27. JAVASCRIPT

  28. “Networks, CPUs and disks all hate you. On the client,

    you pay for what you send in ways you can't easily see” - Alex Russell, Chrome
  29. http://beta.httparchive.org Using Dev Tools mobile emulation, Moto G4 calibrated CPU,

    Cable (5/1mbps, 28ms) STATE OF JAVASCRIPT ON MOBILE 1MB 600KB+ 300KB+ 10% sites 25% sites 50% sites
  30. None
  31. None
  32. None
  33. JavaScript has a cost. Fast = Fast at Parse Eval

    Download On mobile devices
  34. 2017 JavaScript Parse Costs Average Phone ~1MB JavaScript (uncompressed)

  35. JavaScript Parse Cost On Mobile - CNN ~9s difference to

    the A11 With thanks to Pat Meenan
  36. PRPL Pattern USED BY SITES LIKE SUPPORTED BY CLIs

  37. Where do mobile sites spend their time loading? With thanks

    to Camillo and Mathias @ V8 Average Housing Forbes Treebo Twitter Trivago Lancome Tech Today OLACabs Wego Konga
  38. Removing unused code can reduce network transmission times, CPU-intensive code

    parsing, and memory overhead
  39. CODE COVERAGE

  40. 40% SITES MAY USE ONLY OF THE JAVASCRIPT THEY LOAD

    UPFRONT. With thanks to fmeawad@chromium.org JS CODE COVERAGE OF TOP 50 SITES
  41. LEARN FROM GAME DEVELOPERS

  42. BAKE ONLY WHAT A SECTION REQUIRES INTO BUNDLES THAT CAN

    BE LOADED AS NEEDED. n
  43. Code-splitting // Defines a “split-point” for a separate bundle require.ensure([],

    () => { const profile = require('./UserProfile', cb); }); import('./UserProfile') .then(loadRoute(cb)) .catch(errorLoading) Webpack 2+ Webpack 1 Also see Splittable, Closure Compiler or Browserify
  44. Minify _everything_ Babelified ES5 w/Uglify ES2015+ with babel-minify css-loader +

    minimize:true Code-splitting Dynamic import() Route-based chunking Tree-shaking Webpack 2+ with Uglify RollUp DCE w/ Closure Compiler Optimize “Vendor” libs NODE_ENV=production CommonsChunk + HashedModuleIdsPlugin() Transpile less code babel-preset-env + modules:false Browserlist useBuiltIns: true Scope Hoisting: Webpack 3 RollUp Strip unused Lodash modules lodash-webpack-plugin babel-plugin-lodash Fewer Moment.js locales ContextReplacementPlugin()
  45. None
  46. Pinterest’s old Mobile Site - 1st load First Paint: 4.2s

    First Meaningful Paint: 6.2s Time To Interactive: 23s
  47. Pinterest’s new Mobile Site - 1st load First Paint: 1.8s

    First Meaningful Paint: 5.1s Time To Interactive: 5.6s JS Bundles: 620KB ➡ 150KB CSS Bundles: 150KB ➡ 6KB inline P90 for Pin pages: 20s ➡ 6.5s
  48. Webpack Bundle Analyzer: Before splitting out common async route code

  49. Webpack Bundle Analyzer: After moving out common code from async

    chunks into entryChunk 60-90% decrease in size of async route chunks (e.g 13.9KB ➡ 1KB) 20% increase in size of entry (59KB ➡ 71KB)
  50. INTRODUCE WORKFLOWS THAT FORCE EVERYBODY TO THINK ABOUT LOADING TIMES

    FROM THE BEGINNING. n
  51. None
  52. Performance Budgets for JS Budgets Tinder tries not to exceed

    Vendor Async Other 155KB 55KB 35KB CSS 20KB
  53. import A from '../A';
 import B from '../B'; 
 const

    route = [
 {
 route: '/',
 regions: {
 side: A,
 main: B
 }
 }
 ]; JavaScript Route-based code-splitting Before main.js A B
  54. import Loadable from ‘react-loadable'; 
 const A = Loadable({
 loader:

    () => import('../A' /* webpackChunkName: "pc-r-A" */),
 loading: () => null
 }); 
 const B = Loadable({
 loader: () => import('../B' /* webpackChunkName: "pc-r-B" */),
 loading: () => null
 });
 
 const route = [
 {
 route: '/',
 regions: {
 side: A,
 main: B
 },
 preload: [ /* next page chunk to preload*/ ]
 } React Loadable CommonsChunkPlugin After A.js B.js
  55. JavaScript Route-based code-splitting + budgets Before Main bundle size: 166kb

    DOMContentLoad: 5.46s load: 11.91s After Main bundle size: 101kb DOMContentLoad: 4.69s load: 4.69s
  56. CACHING

  57. * Chrome has 4+ caches. The above reflects the main

    two - the HTTP and memory caches Chrome’s Cache Hit Rates
  58. Cache-Control Policies /page HTML /style.3da37df.css CSS /script.8sd34ff.js JavaScript /photo.jpg Image

    Cache-Control: max-age=31536000 Cache-Control: no-cache Cache-Control: private, max-age=31536000 Cache-Control: max-age=86400 https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching
  59. HTTP Caching Checklist Use consistent URLs and minimize resource churn

    Provide a validation token (ETag) to avoid transferring unchanged bytes Identify resources that can be cached by intermediaries (like CDNs) Determine the optimal cache lifetime of resources (max-age) Consider a Service Worker for more control over your repeat visit caching 1. 2. 3. 4. 5. bit.ly/caching-checklist
  60. None
  61. Before Service Worker After Service Worker

  62. https://jakearchibald.com/2016/caching-best-practices/

  63. PRIORITY

  64. http://beta.httparchive.org Using Dev Tools mobile emulation, Moto G4 calibrated CPU,

    Cable (5/1mbps, 28ms) TIME TO INTERACTIVE ON MOBILE 35s 22s 14s 10% sites 25% sites 50% sites
  65. None
  66. None
  67. None
  68. None
  69. None
  70. <link rel="preload" as="script" href="bundle.js"> Chrome 50 Safari 11 Firefox WIP

  71. <link rel="preload" href="movies.json" as="fetch" crossorigin="use-credentials">
 <script>
 (async () => {


    try {
 const response = await fetch(new Request("movies.json", {credentials: "include"}));
 const data = await response.json();
 console.log(data);
 } catch (exception) {
 console.log("Booo");
 }
 })();
 </script> I have critical resources I want to load earlier than discovery. Chrome 62
  72. How are sites are using link rel=preload? BBC News -

    Stylesheets
  73. BBC News - using <link rel=preload> for their stylesheets

  74. 36% improvement <link rel=preload>

  75. <link rel=preload as=script> before <link rel=preload as=script> after Reduce first

    paint by 500ms, load time by 1 second
  76. <link rel=preload as=image> ~1s saving

  77. None
  78. Link element Link header

  79. None
  80. None
  81. Express + HTTP/2 Push Headers const express = require('express'), let

    app = express(); app .use('/js', express.static('js')) .get('/', function (req, res) { res.set('Link', ` </style.css>; rel=preload; as='style', </js/vendor.bundle.js>; rel=preload; as='script', </js/app.bundle.js>; rel=preload; as='script'`)
  82. shop.polymer-project.org

  83. HTTP/2 with 3G ~8s

  84. <5s

  85. HTTP/2 SERVER PUSH, BASICALLY REMY

  86. None
  87. None
  88. None
  89. https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/

  90. AUTOMATE https://github.com/addyosmani/critical INLINE CRITICAL CSS Critical - extract & inline

    critical CSS using Chrome Headless <head> <style> /* critical */ </style> </head> <script> /* loadCSS */ </script> inline async
  91. Baseline Critical CSS Preload H/2 Push https://speakerdeck.com/patrickhamann/css-and-the-first-meaningful-paint-css-conf-eu-may-2017

  92. PAGE WEIGHT

  93. http://beta.httparchive.org Using Dev Tools mobile emulation, Moto G4 calibrated CPU,

    Cable (5/1mbps, 28ms) WEB PAGE WEIGHT ON MOBILE 5.4MB 2.9MB 1.4MB 10% sites 25% sites 50% sites
  94. How many sites do not serve content compressed? ~30%

  95. GZIP, BROTLI, ZOPFLI Compress!

  96. Apache NGINX gzip on; gzip_vary on; gzip_comp_level 6; gzip_http_version 1.1;

    gzip_proxied any; gzip_min_length 256; gzip_buffers 16 8k; gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript; AddOutputFilterByType DEFLATE text/plain AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE application/xml AddOutputFilterByType DEFLATE application/xhtml+xml AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/x-javascript .htaccess nginx.conf
  97. None
  98. Brotli Improved load time by 7% in India & 4%

    U.S bit.ly/linkedin-brotli Decreased the size of static assets by 20% bit.ly/dropbox-brotli 17% improvement for largest JS bundles bit.ly/certsimple-brotli 1.5 petabytes (million gigs) saved a day bit.ly/playstore-brotli
  99. Brotli Use: Mostly JavaScript, CSS and HTML From “Tracking the

    performance of the web with HTTP Archive”
  100. Remove unnecessary downloads The fastest and best-optimized resource is a

    resource not sent. Inventory your own assets and third-party assets on your pages. Measure the perf of each asset: its value and its technical performance. Determine if the resources are providing sufficient value. Lazy-load/defer resources that are non-critical as much as possible. 1. 2. 3. 4. 5.
  101. WEB FONTS

  102. http://beta.httparchive.org Using Dev Tools mobile emulation, Moto G4 calibrated CPU,

    Cable (5/1mbps, 28ms) WEB FONT WEIGHT ON MOBILE 200KB 100KB 80KB 10% sites 25% sites 50% sites
  103. None
  104. None
  105. https://meowni.ca/font-style-matcher/

  106. https://www.zachleat.com/web/comprehensive-webfonts/ “Comprehensive Web Fonts”

  107. @font-face {
 font-family: 'Roboto';
 font-display: optional;
 src: url(Roboto.woff) format('woff'),
 url(Roboto.eot)

    format('eot');
 font-weight: 400;
 font-style: normal;
 } If my Web Fonts can’t load quickly, don’t load them at all. Chrome 60 Safari WIP Firefox WIP
  108. None
  109. <link rel="preload" as="font" href="font.woff" type="font/woff">

  110. Heaviest use of rel=preload is for Web Fonts HTTPArchive

  111. Preloading Web Fonts = 50% (1.2s) improvement in time-to-text-paint

  112. Use System Fonts when you can The fastest font is

    one that doesn’t need to load. Try font-display: optional; If a Web Font can’t load fast, load a fallback instead. If the Web Font is cached, it’ll get used the next time the user loads the page. Try <link rel=preload as=font> to request Web Fonts with a higher priority If Web Fonts are a critical to your UX, preload them to minimize FOIT. Try subsetting to limit the range of Web Font characters needed Subsetting removes characters & Open-Type features from fonts, reducing file size. Google Fonts, TypeKit & Font Squirrel support it. Be careful with use. Try the CSS Font Loading API if you need more control Track font download progress & apply once fetched, manipulate font faces and override default lazy load behavior. S Have a Web Font Loading Strategy @addyosmani
  113. IMAGES

  114. http://beta.httparchive.org Using Dev Tools mobile emulation, Moto G4 calibrated CPU,

    Cable (5/1mbps, 28ms) IMAGE WEIGHT ON MOBILE 3.9MB 1.9MB 0.8MB 10% sites 25% sites 50% sites
  115. Image Quality Matters q=80 is a good baseline for web

  116. JPEG Encoders

  117. https://imageoptim.com www.xnview.com/en/xnconvert/

  118. Adapt intelligently H E I G H T Size appropriately

    WIDTH IMAGE DECODE Compress carefully Take care with tools Prioritize critical images HIGH LOW Lazy-load the rest Choose the right format Image Optimisation
  119. None
  120. None
  121. Heavy image decode Lower image decode

  122. Data Saver Mode introduced up to 70% savings for Twitter

    Lite Do this with the browser using the Save-Data client hint
  123. ESSENTIAL IMAGE OPTIMIZATION BOOK https://images.guide

  124. STAGE 3: MONITOR REMY

  125. EVERYONE IS RESPONSIBLE FOR performance.

  126. 5s

  127. None
  128. None
  129. Performance Budget Tools CALIBRE BUNDLESIZE SPEEDCURVE

  130. bit.ly/perf-budgets REAL-WORLD WEB PERF BUDGETS

  131. PERFORMANCE IS A JOURNEY. LOTS OF SMALL CHANGES CAN LEAD

    TO BIG GAINS. n
  132. KEEP RACING TOWARDS BETTER PERF REMY

  133. Thank you