Skip to content

Instantly share code, notes, and snippets.

@kkas
Last active June 8, 2022 18:13
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kkas/919fcf835a58ef610495 to your computer and use it in GitHub Desktop.
Save kkas/919fcf835a58ef610495 to your computer and use it in GitHub Desktop.
Website Performance Optimization (Note to myself)Some are taken from the online courses and some are taken from the links.

Website Performance Optimization

Why should I profile the site on my phone?

  • Mobile phones on the other hand are much more resource constrained: slower CPUs, less RAM and GPU memory, higher connection latencies, and so on. As a result, you should always try to profile and debug your site on mobile hardware to get a better and closer picture of how your users will experience your site on their handset.

Critical Rendering Path

https://developers.google.com/web/fundamentals/performance/critical-rendering-path/?hl=en

  • is the sequence of steps that the browsers go through to convert the HTML, CSS, and JavaScript into actual pixels on the screen.
  • if we can optimize this, then we can make our pages render fast, which makes for happier users.

Constructing Object Model

  • DOM (Document Object Model)
    • Bytes -> Charactors -> Tokens -> Nodes -> Object Model
    • HTML will be transformed into DOM
  • CSSOM (CSS Object Model)
    • Bytes -> Charactors -> Token -> Nodes -> CSSMOS
    • CSS will be transformed into CSSOM
    • Every browsers have default set of styles.
    • In DevTools, "Recalculate Style"

Render-tree construction, Layout, and Paint

  • The CSSOM and DOM trees are combined into a rendering tree, which then used to compute the layout of each visible element and serves as input to the paint process which renders the pixels to screen.
  • In DevTools, "Layout": The render tree construction and position and size calculation

Recap of the steps the browser go through:

  1. Process HTML markup and build the DOM tree.
  2. Process CSS markup and build the CSSOM tree.
  3. Combine the DOM and CSSOM into a render tree.
  4. Run layout on the render tree to compute geometry of each node.
  5. Paint the individual nodes to the screen.

Render Blocking CSS

https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-blocking-css?hl=en

  • By default, All the HTML and CSS are render blocking.
  • FOUC (Flash of Unstyled Content)
    • HTML without CSS
  • CSS is a render blocing resource, get it down to the client as soon and as quick as possible to optimize the time to first render
  • Use "media query" to prevent downloading unnecessary CSS. For example,
<link href="style.css" rel="stylesheet">
<link href="print.css" rel="stylesheet" media="print">
<link href="other.css" rel="stylesheet" media="(min-width: 40em)">
  • note that “render blocking” only refers to whether the browser will have to hold the initial rendering of the page on that resource.In either case, the CSS asset is still downloaded by the browser, albeit with a lower priority for non-blocking resources.

Adding Interactivity with JavaScript

https://developers.google.com/web/fundamentals/performance/critical-rendering-path/adding-interactivity-with-javascript?hl=en

  • JavaScript can also block DOM construction and delay when the page is rendered. Making JavaScript async and eliminating any unnecessary JavaScript from the critical rendering path can deliver optimal performance.

  • JavaScript can query and modify DOM and CSSOM.

  • JavaScript execution blocks on CSSOM

  • JavaScript blocks DOM construction unless explicitly declared as async

  • JavaScript is executed at the exact point where it is inserted in the document.

  • When the HTML parser encounters a script tag, it pauses its process of constructing the DOM and yields control over to the JavaScript engine

  • when the JavaScript engine has finished running, the browser then picks up from where it left off and resumes the DOM construction.

  • In other words, executing our inline script blocks DOM construction, which will also delay the initial render.

  • JavaScript can also modify CSSOM. Therefore, there is a race condition. the browser will delay script execution until it has finished downloading and constructing the CSSOM, and while we're waiting, the DOM construction is also blocked.

  • One exception: If we put our JavaScript above our CSS, then it will execute without blocking on CSS.

Recap

  1. The location of the script in the document is significant.
  2. DOM construction is paused when a script tag is encountered and until the script has finished executing.
  3. JavaScript can query and modify the DOM and CSSOM.
  4. JavaScript execution is delayed until the CSSOM is ready.
  • <script> tag and inline JacaScript snippet works identical and behave in the same way, except in the case of an external JavaScript file the browser will also have to pause and wait for the script to be fetched from disk, cache, or a remote server, which can add tens to thousands of milliseconds of delay to the critical rendering path.
  • Use 'async' to let the browser knows it should not block tehe DOM construction while it waits for the script to become available. Otherwise, the browser will assume the worst case senario and wait for the script becomes ready.
  <html>
      <head>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link href="style.css" rel="stylesheet">
        <title>Critical Path: Script Async</title>
      </head>
      <body>
        <p>Hello <span>web performance</span> students!</p>
        <div><img src="awesome-photo.jpg"></div>
        <script src="app.js" async></script>
      </body>
    </html>

Measuring the Critical Rendering Path with Navigation Timing

https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp?hl=en

  • Navigation Timing API provides high resolution timestamps for measuring CRP.
  • Browser emits series of consumable events which capture various stages of the CRP.

See: https://developers.google.com/web/fundamentals/performance/critical-rendering-path/images/dom-navtiming.png

  • domLoading: this is the starting timestamp of the entire process, the browser is about to start parsing the first received bytes of the HTML document.
  • domInteractive: marks the point when the browser has finished parsing all of the HTML and DOM construction is complete.
  • domContentLoaded: marks the point when both the DOM is ready and there are no stylesheets that are blocking JavaScript execution - meaning we can now (potentially) construct the render tree.
    • Many JavaScript frameworks wait for this event before they start executing their own logic. For this reason the browser captures the EventStart and EventEnd timestamps to allow us to track how long this execution took.
  • domComplete: as the name implie, all of the processing is complete and all of the resources on the page (images, etc.) have finished downloading -i.e. the loading spinner has stopped spinning.
  • loadEvent: as a final step in every page load the browser fires an "onload" event which can trigger additional application logic.
    <html>
      <head>
        <title>Critical Path: Measure</title>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link href="style.css" rel="stylesheet">
        <script>
          function measureCRP() {
            var t = window.performance.timing,
              interactive = t.domInteractive - t.domLoading,
              dcl = t.domContentLoadedEventStart - t.domLoading,
              complete = t.domComplete - t.domLoading;
            var stats = document.createElement('p');
            stats.textContent = 'interactive: ' + interactive + 'ms, ' +
                'dcl: ' + dcl + 'ms, complete: ' + complete + 'ms';
            document.body.appendChild(stats);
          }
        </script>
      </head>
      <body onload="measureCRP()">
        <p>Hello <span>web performance</span> students!</p>
        <div><img src="awesome-photo.jpg"></div>
      </body>
    </html>

Analyzing Critical Rendering Path Performance

https://developers.google.com/web/fundamentals/performance/critical-rendering-path/analyzing-crp?hl=en

  • The gap between the end of the HTML download and the blue vertical line (DOMContentLoaded) is the time it took the browser to build the DOM tree

  • When we talk about CRP, it means that we talk about HTML, CSS, and JavaScript, not the images since they don't block the initial render of the page. (Although we should try our best we get the images painted as soon as possible.)

  • "load" event (a.k.a 'onload') is blocked on the image, since 'load' event marks the point when all resources required by the page have been downloaded and processed.

  • Performance Patterns

    • Critical Resource: resource that may block initial rendering of the page.
    • Critical Path Length: number of roundtrips, or the total time required to fetch all of the critical resources.
    • Critical Bytes: total amount of bytes required to get to first render of the page, which is the sum of the transfer filesizes of all critical resources. Our first example with a single HTML page contained a single critical resource (the HTML document), the critical path length was also equal to 1 network roundtrip (assuming file is small), and the total critical bytes was just the transfer size of the HTML document itself.
  • The advantages of making the script async:

    • The script is no longer parser blocking and is not part of the critical rendering path
    • Because there are no other critical scripts, the CSS also does not need to block the domContentLoaded event
    • The sooner the domContentLoaded event fires, the sooner other application logic can begin executing

Optimizing the Critical Rendering Path

  • In order to deliver the fastest possible time to first render, we need to optimize three variables:

    • Minimize the number of critical resources.
      • The fewer of these resources there are on the page, the less work the browser has to do to get content on the screen, and the less contention there is for CPU and other resources.
    • Minimize the number of critical bytes.
      • the fewer critical bytes the browser has to download, the faster it can get to processing the content and get it visible on the screen.
      • To reduce the nuber of resources (eliminate them or make them non-critical), and also ensure that we minimize the transfer size by compressing and optimizing each resource.
    • Minimize the critical path length.
  • The general sequence of steps to optimize the critical rendering path:

    1. Analyze and characterize your critical path: number of resources, bytes, length.
    2. Minimize number of critical resources: eliminate them, defer their download, mark them as async, etc.
    3. Optimize the order in which the remaining critical resources are loaded: you wan to download all critical assets as early as possible to shorten the critical path length.
    4. Optimize the number of critical bytes to reduce the download time (number of roundtrips).

PageSpeed Rules and Recommendations

https://developers.google.com/web/fundamentals/performance/critical-rendering-path/page-speed-rules-and-recommendations?hl=en

  • Eliminate render-blocking JavaScript and CSS
  • Optimize JavaScript Use
    • Prefer async JavaScript resources
    • Avoid synchronous server calls
      • if the script can be made async, it also means it is not essential for the first render
      • consider loading async scripts after the initial render
    • Avoid synchronous server calls
      • use navigator.sendBeacon() method to limit data sent by XMLHtttpRequests in unload handlers.
<script>
  function() {
    window.addEventListener('pagehide', logData, false);
    function logData() {
      navigator.sendBeacon(
        'https://putsreq.herokuapp.com/Dt7t2QzUkG18aDTMMcop',
        'Sent by a beacon!');
    }
  }();
</script>
* Because many browsers require such requests to be synchronous, they can slow page transitions, somethimes noticeably.
* the new `fetch()` method provides an easy way to asynchronously request data.
<script>
fetch('./api/some.json')  
  .then(  
    function(response) {  
      if (response.status !== 200) {  
        console.log('Looks like there was a problem. Status Code: ' +  response.status);  
        return;  
      }
      // Examine the text in the response  
      response.json().then(function(data) {  
        console.log(data);  
      });  
    }  
  )  
  .catch(function(err) {  
    console.log('Fetch Error :-S', err);  
  });
</script>
* Defer parsing JavaScript
* Avoid long running JavaScript
  • Optimize CSS Use CSS is required to construct the render tree and JavaScript will often block on CSS during initial construction fo the page. You should ensure that any non-essential CSS is marked as non-critical (e.g. print and other media queries), and that the amount of critical CSS and the time to deliver it is as small as possible.
    • Put CSS in the document head
    • Avoid CSS imports
      • CSS import (@import) directives should be avoided because they introduce additional roundtrips into the critical path.
    • Inline render-blocking CSS
      • For best performance, you may want to consider inlining the critical CSS directly into the HTML document. This eliminates additional roundtrips in the critical path and if done correctly can be used to deliver a "one roundtrip" critical path length where only the HTML is a blocking resource.

Note

How to know what makes it slow?

  • Mesure first, then Optimize - Golden Rule -

Optimization

Resource minification

https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer#minification-preprocessing--context-specific-optimizations

  • Summary

    • Content-specific optimization can significantly reduce the size of delivered resoureces.
    • Content-specific optimization are the best applied as part of your build/release cycle.
  • more details

    • How to minify?
      • Remove comments
      • Combine rules in CSS where it is possible
        <style>
           .awesome-container { font-size: 120% }
           .awesome-container { width: 50% }
        </style>
      • Remove all the whitespaces
    • Why preprosessing / minification / content-aware optimization can be powerful?
      • A general purpose compressor would never know how to strip the comments, collapse the CSS rules, or dozens of other content-specific optimizations.
    • The techniques of minifing text-based assets can be applied to other non-text assets, such as images, videos, and other content types all contain their own forms of metadata and various payloads.
      • In practice, removing metadata from a image can reduce tens of kilobytes.

compress

https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer#text-compression-with-gzip

  • Summary

    • GZIP performs best on text-based assets: CSS, HTML, JavaScript
    • All modern browsers support GZIP compression and will automatically request it.
    • Your server needs to be configured to enable GZIP compression
    • Some CDNs require special care to ensure that GZIP is enabled
  • How to GZIP? The HTML5 Boilerplate project contains sample configuration files for all the most popular servers with detailed comments for each configuration flag and setting: find your favorite server in the list, look for the GZIP section, and confirm that your server is configured with recommended settings.

  • How to know if the contents is correctly gzipped??

Recap

  1. Apply content-specific optimization first: CSS, JS, and HTML minifiers.
  2. Apply GZIP to compress the minified output.

HTTP Caching

  • every browser ships with an implementation of an HTTP cache. We just need to make sure that each server response provides correct HTTP header derectives to instruct the browser on when and for how long the response can be cached by the browser.

  • If you are using a Webview to fetch and display web content in your application, you may need to provide additional configuration flags to ensure that the HTTP cache is enabled, its size is set to a reasonable number to match your use case, and that the cache is persisted.

  • https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/images/http-request.png

  • Etag provides a validation token that can be used after the response has expired to check if the resource has been modified.

  • summary

    • Validation token is communicated by the server via the ETag HTTP header
    • Validation token enables efficient resource update checks: no data transfer if the resource has not changed.
    • How?
      • The browser checks Cache-Control: max-age=xxx, where xxx is inseconds, in the HTTP header from the response.
      • If it is expired, then the browser sends the token in ETAG for the next request with If-None-Match.
      • If the resource has not been modified, the server returns the 304 Not Modified response code.
    • We just need to make sure the servers return the ETag tokens.
      • The browser automatically takes care of the flow above.

Quick Notes:

  • Minimize Bytes
    • Minify, Compress, Cache
      • HTML, CSS, JavaScript
    • Minimize use of render blocking resources (CSS)
      • Use media queries on <link> to unblock rendering
      • Inline CSS
    • Minimize use of parser blocking resources (JS)
      • Defer JavaScript execution
      • Use async attribute on <script>

Preload Scaner

  • Instead of waiting for buildng CSSOM and execution of JavaScript, the preload scanner skims through the document and finds the necessary assets while the parser is blocked.
  • a.k.a speculative or look-ahead pre-parser

CRP metrics comprehension

  • The critical path length
  • The number of Critical Resources
  • The number of Critical Bytes

(See more details at "Performance Pattern above")

Links:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment