Client Side Caching
Published: 2023-05-04 14:11:00

## Introduction to Client-Side Caching

Client-side caching is a technique used to store frequently accessed web content on the client's machine, reducing the need for server round-trips and improving website performance. This technique is needed because network round-trips can cause latency and impact website performance. 

Let's say you have a favourite book that you like to read every day. Instead of going to the library every time you want to read the book, you keep a copy of it on your bedside table. This way, you can pick up the book and start reading it right away, without having to go to the library every time.

In this scenario, the library is like the server where the book is stored, and your bedside table is like the cache where you've stored a copy of the book. Just like caching a website or app on your device, caching the book on your bedside table makes it much easier and faster to access, since you don't have to go all the way to the library every time you want to read it. 

The primary advantage of client-side caching is faster website performance, which can lead to improved user experience, increased engagement, and higher conversion rates. Caching can also help reduce server load, reduce network traffic, and improve scalability.

Here is a code snippet to better explain how caching works

```
const cache = {
    '1-4': 1
}
function cachedComputation(a, b) {
    const key = `${a}-${b}`;
    if (cache[key]) {
      console.log("from cache");
      cache[key] = a + b;
      return cache[key];
    }
    for(let i = 0; i < 10000000000; i++) {}
    console.log("from database");
    cache[key] = a + b;
    return cache[key];
}
cachedComputation(1, 7); // console prints from database
cachedComputation(1, 4); // console prints from cache
cachedComputation(1, 7); // console prints from cache since we have already ran the function with 1, 7 as arguments and stored in the cache
```
 
This code demonstrates how to use caching to speed up function computations. The `cache` object stores the results of previous function calls, so when the function is called with the same arguments, it can return the result from the cache instead of recomputing it. If the result is not in the cache, the function performs the computation and stores the result in the cache for future use.

## How Client-Side Caching Works
Caching works by storing a copy of a web resource on the client's machine and reusing it when the resource is requested again. HTTP caching uses caching headers to control caching behaviour and determine whether a client can use a cached copy or must fetch a fresh copy from the server.

Browsers can also cache web resources automatically using built-in caching algorithms. Caching headers, such as Cache-Control, Expires, and ETag, provide granular control over caching behaviour and can be set by the server.

## Techniques for Implementing Client-Side Caching
Cache-Control is a caching header used to set cache expiration and validation rules. It provides granular control over caching behaviour and can be used to set a maximum age for cached resources, control cache revalidation, and prevent caching altogether.

Cache-busting is a technique used to force clients to fetch fresh copies of resources by changing the resource URL or version. This technique can be used to invalidate caches when the content has changed, ensuring that clients see the latest version.

Here's an example of how to implement cache-busting in a web application using a unique file name or query string parameter.



1. Unique file name approach:

```
<!DOCTYPE html>
<html>
<head>
  <title>Cache-Busting Example</title>
  <link rel="stylesheet" href="/styles.css?v=1.0">
</head>
<body>
  <h1>Cache-Busting Example</h1>
  <script src="/script.js?version=1.0"></script>
</body>
</html>
```

In this example, we've added a query string parameter to the file name for both the stylesheet and script tags. By changing the value of the `version` parameter, we force the client to download a new version of the file, even if it is already cached.

2. Query string parameter approach:

```
<!DOCTYPE html>
<html>
<head>
  <title>Cache-Busting Example</title>
  <link rel="stylesheet" href="/styles.css?v=<%= Date.now() %>">
</head>
<body>
  <h1>Cache-Busting Example</h1>
  <script src="/script.js?version=<%= Date.now() %>"></script>
</body>
</html>
```

In this example, we're using a server-side template engine like EJS to dynamically generate a new value for the `version` parameter based on the current timestamp. This has the same effect as the previous example, but with the added benefit of being able to generate unique cache-busting values on the server-side.

By using either of these techniques, you can invalidate caches and ensure that clients always receive the latest version of your files. It's important to note that cache-busting can impact performance, as clients will need to download new files every time the cache is invalidated. Therefore, it's important to use cache-busting sparingly and only when necessary.

## Best Practices for Client-Side Caching

1. Setting Maximum Age and Expiration vs Validation
When setting cache headers, it's important to balance the benefits of caching with the need for fresh content. Setting a maximum age for cached resources can help ensure that clients get fresh content regularly. Expiration-based caching is useful for resources that don't change frequently, while validation-based caching is useful for resources that change frequently.


2. Avoiding Common Pitfalls
Common pitfalls of client-side caching include cache poisoning, cache inconsistency, and cache invalidation. To avoid these issues, it's important to set cache headers correctly, use cache-busting techniques, and ensure that cached resources are consistent and up-to-date.

## Examples of Client-Side Caching Implementation

1. CSS and JavaScript Files
CSS and JavaScript files are critical to website performance, and caching these resources can help reduce page load times and improve user experience.

2. Images and Other Resources
Images and other resources, such as videos, can benefit greatly from client-side caching. By caching these resources, clients can load them faster, reducing page load times and improving performance.

## Testing and Measuring Client-Side Caching Performance

Tools such as Chrome DevTools, Firebug, and Fiddler can be used to test caching performance by analyzing the caching headers and checking whether resources are being cached correctly. These tools can also be used to simulate different network conditions and test caching behaviour under different scenarios.

Network latency and time to first byte are critical factors in website performance and can be measured using tools such as Pingdom, GTmetrix, and WebPageTest.

In conclusion, as the web evolves, new caching techniques and technologies are likely to emerge. The use of content delivery networks (CDNs), progressive web apps (PWAs), and service workers are likely to play a larger role in client-side caching and website performance optimisation in the future. It's important for developers to stay up-to-date on the latest caching best practices and tools to ensure that their websites are fast, reliable, and engaging.