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

HTML Optimization for Web Performance

Shogo Sensui
November 16, 2019

HTML Optimization for Web Performance

2019年11月16日に開催された Frontend Conference Fukuoka 2019 https://eventon.jp/18416/ の「HTML Optimization for Web Performance」のセッション資料です。また、セッション内容を書き起こした解説記事はこちら https://tech.mercari.com/entry/2019/12/02/000000 です。

Shogo Sensui

November 16, 2019
Tweet

More Decks by Shogo Sensui

Other Decks in Technology

Transcript

  1. Shogo SENSUI (@1000ch) • Software Engineer focusing on the web

    technology • Engineering Manager and Tech Lead at Merpay, Inc. • This is the second in Fukuoka. My first time was 2014.01.25
  2. 3 steps for HTML Optimization 1. Understanding Critical Rendering Path

    2. Measuring Performance Metrics 3. Optimizing HTML
  3. Browser Loading Process 1. Download HTML a. Parse HTML to

    construct DOM 2. Download subresources a. Parse CSS to construct CSSOM b. Parse and evaluate JavaScript synchronously c. Parse images, audios, videos, etc. 3. Create Render Tree using DOM and CSSOM a. Wait for DOM and CSSOM b. Layout nodes c. Paint nodes Construct DOM Tree Construct CSSOM Tree Create Render Tree Layout & Paint Execute JavaScript
  4. 1st step. Load HTML • Download HTML from requested URL

    • Parse HTML to construct DOM tree • HTML will be evaluated from the top <html> <head> <meta charset="utf-8"> <title>Basic HTML structure</title> <link rel="stylesheet" href="foo.css"> <link rel="stylesheet" href="bar.css"> </head> <body> <img src="hero.jpg"> <script src="app.js"></script> <script defer src="3rd-party.js"></script> </body> </html>
  5. 2nd step. Load subresources • Download subresources specified in HTML

    elements ◦ <link> ◦ <script> ◦ <img> ◦ <iframe> ◦ etc… • CSS will be downloaded and evaluated asynchronously • JavaScript will be downloaded and evaluated synchronously ◦ will affect DOM and CSSOM <html> <head> <meta charset="utf-8"> <title>Basic HTML structure</title> <link rel="stylesheet" href="foo.css"> <link rel="stylesheet" href="bar.css"> </head> <body> <img src="hero.jpg"> <script src="app.js"></script> <script defer src="3rd-party.js"></script> </body> </html>
  6. 3rd step. Render pages • Wait for DOM and CSSOM

    are completely parsed • Create Render Tree using DOM and CSSOM • Display page ◦ Layout nodes ◦ Paint nodes without CSSOM with CSSOM
  7. Wait, Load and DOMContentLoaded events? • They are not user-centralized

    performance metrics • We want to score the real-user experience
  8. Speed Index • Visual Progress Score of the page •

    Introduced at WebPagetest ◦ Speed Index - WebPagetest Documentation ◦ Further information in Japanese: Speed IndexというWebパフォーマンスの指標 • Slightly difficult to understand and calculate. We need simpler metrics!
  9. First Paint and First Contentful Paint • First Paint: When

    anything on the first view is visible to the users • First Contentful Paint: When any content on the first view is visible to the users
  10. Largest Contentful Paint • Timing when the biggest content on

    the first view was painted • These elements are considered ◦ <img>, <image> in <svg>, <video> ◦ An element with a background image loaded via CSS url() ◦ Block-level elements
  11. Long Tasks • The process make the main thread busy

    • Long Tasks will cause the UI to freeze ◦ Delay time-to-interactive ◦ Input latency
  12. Time to Interactive • How long it takes a page

    to become fully interactive • Use GoogleChromeLabs/tti-polyfill to measure ◦ It requires Long Tasks API is supported
  13. PerformanceObserver API • Enables you to measure the web application

    performance on the user’s device const po = new PerformanceObserver(list => { for (const entry of list.getEntries()) { // `entry` is a PerformanceEntry instance. console.log(entry.entryType); console.log(entry.startTime); console.log(entry.duration); } }); // Start observing the entry types you care about. po.observe({ entryTypes: ['resource', 'paint'] });
  14. To measure FP, FCP, LCP: const observer = new PerformanceObserver(list

    => { for (const entry of list.getEntries()) { ga('send', 'event', { eventCategory: 'Performance Metrics', eventAction: entry.name, eventValue: entry.startTime + entry.duration, nonInteraction: true }); } }); // Start observing the entry types, FP, FCP, LCP. observer.observe({ entryTypes: ['paint', 'largest-contentful-paint'] });
  15. To measure LongTasks: const observer = new PerformanceObserver(list => {

    for (const entry of list.getEntries()) { ga('send', 'event', { eventCategory: 'Performance Metrics', eventAction: 'longtask', eventValue: Math.round(entry.startTime + entry.duration), eventLabel: JSON.stringify(entry.attribution), }); } }); // Start observing the entry types, Long Task. observer.observe({ entryTypes: ['longtask'] });
  16. Lighthouse • Automated auditing, performance metrics, and best practices for

    the web. • Bundled on Chrome DevTools • GoogleChrome/lighthouse to use from the command line • GoogleChrome/lighthouse-ci to integrate with CI
  17. WebPageTest • Measuring and analyzing the performance of web pages

    • Open-sourced at GitHub. • SpeedCurve - WebPageTest wrapper • Calibre - WebPageTest wrapper • The test result of shogosensui.com • The lighthouse test result of shogosensui.com
  18. Optimize CSS Loading 1. Put <link rel=stylesheet> in <head> a.

    To prefer loading CSS and constructing CSSOM b. Putting multiple <link>s is OK! Because browser will load them asynchronously 2. Separate CSS files as possible a. Should NOT be concatenated
  19. Optimize JavaScript Loading 1. Put <script></script> at the end of

    <body> a. To prefer constricting HTML b. To prefer loading subresources 2. Add defer attribute to <script> that loads 3rd party JavaScript a. To prefer rendering the current web page
  20. Basic HTML structure will be... <html> <head> <meta charset="utf-8"> <title>Basic

    HTML structure</title> <link rel="stylesheet" href="foo.css"> <link rel="stylesheet" href="bar.css"> </head> <body> <!-- ... --> <script src="app.js"></script> <script defer src="3rd-party.js"></script> </body> </html>
  21. Preload subresources • Load subresources actively • Specify preload attribute

    for <link> • Preload directive: as attribute to specify the kind of subresource ◦ as=media: Audio, Video ◦ as=script: Script file ◦ as=style: CSS file ◦ as=font: Font file ◦ as=image: Image file ◦ etc... <link rel="preload" href="audio.mp3" as="media"> <link rel="preload" href="app.css" as="style"> <link rel="preload" href="app.js" as="script"> <link rel="preload" href="hero.jpg" as="image">
  22. Preload for module scripts • Load module scripts actively •

    Specify modulepreload attribute for <link> • Further information about modulepreload: ES Modulesを優先 的にロードするmodulepreload <head> <link rel="modulepreload" href="app.mjs"> </head> <!-- ... --> <script type="module" src="app.mjs"></script>
  23. Native lazy-loading for <img> and <iframe> • loading attribute to

    defer the loading of off-screen images and iframes ◦ <img> ◦ <iframe> • Don’t have to use IntersectionObserver and to observe scroll and resize events. <img src="image.png" loading="lazy" alt="…"> <iframe src="https://example.com" loading="lazy"></iframe>
  24. Priority Hints for subresources • importance attribute to suggest the

    resource priority ◦ <link> ◦ <img> ◦ <script> ◦ <iframe> <!-- An image the browser assigns "High" priority, but we don't actually want that. --> <img src="in_viewport_but_not_important.svg" importance="low" alt="..."> <!-- We want to initiate an early fetch for a resource, but also deprioritize it --> <link rel="preload" href="/js/script.js" as="script" importance="low"> <script src="/js/app.js" defer importance="high"></script>
  25. Resource Hints • Load subresources speculatively • DNS Prefetch ◦

    Resolves DNS • Preconnect ◦ Creates TCP connection • Prefetch ◦ Fetches resources • Prefender ◦ Renders HTML page <link rel="dns-prefetch" href="//example.com"> <link rel="preconnect" href="//example.com"> <link rel="preconnect" href="//cdn.example.com" crossorigin> <link rel="prefetch" href="next-page.html" as="document"> <link rel="prefetch" href="lib.js" as="script"> <link rel="prerender" href="next-page.html">
  26. Conslusion 1. Understanding Critical Rendering Path a. Browsers wait for

    DOM and CSSOM are parsed before rendering 2. Measuring Web Performance Metrics a. Use WebPageTest and Lighthouse to audit the web page b. Audit the web page continuously 3. Optimizing HTML a. For initial loading b. For runtime c. Minimizing payload is the premise for both