source type="image/webp" srcSet="/static/c8c5b8fc..." />source type="image/webp" srcSet="/static/c8c5b8fc..." />Why Your Cached JavaScript Is Still Slow and Incurs Performance Overhead
Why Your Cached JavaScript Is Still Slow and Incurs Performance Overhead<!-- --> | <!-- -->Web Performance Tips

Why Your Cached JavaScript Is Still Slow and Incurs Performance Overhead

Posted On: Last Updated On: July 4, 2022
Subscribe to the Web Performance Tips RSS Feed

Web Developers often fixate on optimizing the delivery of assets to the end-user's device, and overlook the computation that takes place on the end-user's device once those assets arrive.

In modern web development, JavaScript-centric SPAs are flooding the user's device with heavy compute. When I meet with teams interested in improving their web product's performance, this concept of client-side compute is often overlooked and I'm met with the following common misconceptions:

  • Our JavaScript is Cached in the Browser, so our page should be fast!
  • Bundle size doesn't matter since the bundles are stored in cache!
  • We just completed our React migration, so we were expecting sizeable performance improvements, especially when the JS is cached!

These couldn't be further from the truth!

The Important of Asset Delivery

Don't get me wrong, for optimal web performance, assets should be optimally delivered and cached!

Web application performance is fundamentally tied to the network and the speed at which we can deliver an asset to the end-user device.

However, even if a web application has all assets optimally delivered and/or cached, that doesn't mean that it will run fast or render quickly when it is needed during runtime on the end-user's device.

Web Application Bottlenecks

At a high level, there are two primary performance bottlenecks on the web:

  1. Networking - the round-trip time to acquire an asset or data payload from a remote server
  2. End-user Device Compute - the amount of computational overhead required on the end-user's device

The latter often overlooked, and nowadays, it's the most important aspect of web performance! When JavaScript is cached, there is no networking cost! However, there is plenty of end-user device compute cost.

Let's take a closer look at the (significant) compute associated with loading and executing cached JavaScript.

A Concrete Scenario

Consider the following React-based SPA:

<body>
    <div id="root"></div>
    <script src="/vendor-bundle.HASH.js" type="text/javascript"></script>
    <script src="/app-bundle.HASH.js" type="text/javascript"></script>
</body>
// app-bundle.HASH.js

ReactDOM.render(<MyApp />, document.getElementById('root'))

A common and insightful optimization would be to apply Cache-Control headers to the app-bundle and vendor-bundle JS files. One might even go further and use a Service Worker to pre-install assets. This would allow users who re-visit the web application to load the JavaScript files from an offline disk cache.

But what happens when these JavaScript files are loaded from cache?

System Overview

The following diagram overviews the process of loading a script from disk cache: