How to Implement a Simple Cache Invalidation System in Vue.js

·

4 min read

How to Implement a Simple Cache Invalidation System in Vue.js

Before we delve into the mechanics of cache invalidation, let's clarify what caching is.

A cache, often referred to as a web cache, is a technology designed to store copies of web content, such as web pages, images, and other resources, closer to end-users. The primary goal of a web cache is to enhance the speed and efficiency of web page loading by reducing the time it takes to fetch web content from remote web servers. This caching mechanism significantly improves the user experience by minimizing latency and conserving bandwidth.

Given this definition, it's evident that utilizing caching can lead to better performance. So, when should we consider invalidating these caches? The necessity for cache invalidation varies depending on different use cases. In my experience, a particular challenge emerged when performing major releases. Some users experienced issues with the platform even after thorough testing before the release. At times, we had to instruct users to force-refresh their browsers to obtain up-to-date data. Clearly, this wasn't an ideal user experience, prompting us to find a solution.

Our approach was straightforward: we needed to automate the process of refreshing the client-side cache whenever a new release of the website occurred. To accomplish this, we explored several methods, but the one that proved effective was to maintain a version data comparison between the local version and the deployed version.

Step 1: Create a version.json File

Begin by creating a version.json file in your Vue project's public folder. The file should contain a key named "version" with a value that can be used for comparison. For this example, let's use a timestamp as the value.

{
  "version": "" // This is where the timestamp comes in
}

Step 2: Implement the Version Comparison in a Composable

Now, let's implement the version comparison logic in a composable. Begin by defining the base URL for your domain:

const baseUrl = import.meta.env.VITE_WEB_URL;

This base URL should point to your website's domain. Next, retrieve the deployed version from your hosted domain:

interface GetVersion {
  version: string;
}

async function getRealVersion(): Promise<GetVersion> {
  const res = await axios.get<GetVersion>(`${baseUrl}/version.json`);
  return res.data;
}

Similarly, obtain the local version from the version.json file:

import localVersion from "../../public/version.json";

function getLocalVersion(): GetVersion {
  try {
    const version = JSON.parse(JSON.stringify(localVersion));
    return version;
  } catch {
    return {
      version: "",
    };
  }
}

Now that we have both versions, we can create a function to compare them:

import { compareAsc, isValid } from "date-fns";

export async function invalidCacheCheck(): Promise<Boolean> {
  const deployedVersion = new Date((await getRealVersion()).version);
  const localVersion = new Date(getLocalVersion().version);

  if (!isValid(localVersion)) {
    return false;
  }

  const newVersionAvailable = compareAsc(deployedVersion, localVersion);

  if (newVersionAvailable === 1) return true;

  return false;
}

Step 3: Implement Cache Invalidation on a Timer

However, the problem with the above function is that it only checks once when called. To address this, we need to perform checks at regular intervals. Here's how you can achieve this:

import { minutesToMilliseconds } from "date-fns";

export async function cacheInvalidator(): Promise<void> {
  const timeIntervalInMinutes = 5;
  const timeIntervalInMillisecond = minutesToMilliseconds(
    timeIntervalInMinutes
  );

  setInterval(async () => {
    const invalidate = await invalidCacheCheck();
    if (invalidate) {
      window.location.reload();
    }
  }, timeIntervalInMillisecond);
}

This function checks if a new version is available in intervals of 5 minutes and, if so, refreshes the client-side cache.

Step 4: Implementation in App.vue

To activate this cache invalidation mechanism, call the cacheInvalidator() function in your App.vue file.

Step 5: Updating the Version with Each Deployment

Lastly, you'll need to ensure that the version is updated with each deployment. You can achieve this by using a script that generates the version.json file. Here's an example script:

#!/bin/bash

# Get the current UTC time
current_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Create a JSON object with the current time
json_data="{ \"version\": \"$current_time\" }"

echo "$json_data" > ./public/version.json

echo "Version has been updated"

Include this script in your build process to automatically update the version in each deployment.

By implementing this cache invalidation system, you can ensure that your Vue.js application always serves the latest content to your users, enhancing their experience and avoiding the need for manual refreshes.