Frontend Development 6 min read

Handling Page Refresh and Cache Updates in Vue SPA with Vite

This article explains why users may still see outdated pages after a Vue SPA deployment, analyzes the caching issues caused by static asset headers, and provides both back‑end coordinated and pure front‑end solutions—including WebSocket, SSE, ETag polling, custom Vite plugins, and example code—to automatically detect and prompt updates.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Handling Page Refresh and Cache Updates in Vue SPA with Vite

Introduction

Boss: The new feature is live, but users still see the old page. Developer: Ask them to refresh or clear cache. Boss: That’s a poor user experience; we need a better way.

Product Introduction

For consumer‑facing (C‑end) applications, textual content often needs updates; old wording can cause public opinion issues, so the page must be refreshed after updates so users see the latest version.

Why the Issue Occurs

The project is a Vue SPA served through Nginx with static resources cached via Cache-Control headers. After a normal front‑end redeployment, users who re‑visit the site receive the newest page.

However, most users stay on the current page; after a front‑end deployment they continue to see the old version until they manually refresh.

Problems Caused

If the back‑end API changes, the old front‑end may cause API errors.

Users may not see the new UI, leading to a bad experience.

Online bugs fixed in the new version remain visible to users still on the old page.

Solution

Front‑end and back‑end cooperation:

WebSocket

SSE (Server‑Sent Events)

Pure front‑end approach (examples use Vite + Vue 3):

Polling HTML ETag / Last‑Modified

In App.vue add the following code:

const oldHtmlEtag = ref();
const timer = ref();
const getHtmlEtag = async () => {
  const { protocol, host } = window.location;
  const res = await fetch(`${protocol}//${host}`, {
    headers: {
      "Cache-Control": "no-cache",
    },
  });
  return res.headers.get("Etag");
};

oldHtmlEtag.value = await getHtmlEtag();
clearInterval(timer.value);
timer.value = setInterval(async () => {
  const newHtmlEtag = await getHtmlEtag();
  console.log("---new---", newHtmlEtag);
  if (newHtmlEtag !== oldHtmlEtag.value) {
    Modal.destroyAll();
    Modal.confirm({
      title: "Detected new version, update?",
      content: "New version content:",
      okText: "Update",
      cancelText: "Cancel",
      onOk: () => {
        window.location.reload();
      },
    });
  }
}, 30000);

Additional file versionData.json is generated by a custom Vite plugin.

Create a custom plugin at /plugins/vitePluginCheckVersion.ts :

import path from "path";
import fs from "fs";
export function checkVersion(version: string) {
  return {
    name: "vite-plugin-check-version",
    buildStart() {
      const now = new Date().getTime();
      const version = {
        version: now,
      };
      const versionPath = path.join(__dirname, "../public/versionData.json");
      fs.writeFileSync(versionPath, JSON.stringify(version), "utf8", (err) => {
        if (err) {
          console.log("Write failed");
        } else {
          console.log("Write succeeded");
        }
      });
    },
  };
}

Import the plugin in vite.config.ts :

import { checkVersion } from "./plugins/vitePluginCheckVersion";
plugins: [
  vue(),
  checkVersion(),
]

Add update‑checking logic in App.vue :

const timer = ref();
const checkUpdate = async () => {
  let res = await fetch('/versionData.json', {
    headers: {
      'Cache-Control': 'no-cache',
    },
  }).then((r) => r.json());
  if (!localStorage.getItem('demo_version')) {
    localStorage.setItem('demo_version', res.version);
  } else {
    if (res.version !== localStorage.getItem('demo_version')) {
      localStorage.setItem('demo_version', res.version);
      Modal.confirm({
        title: 'Detected new version, update?',
        content: 'New version content: ' + res.content,
        okText: 'Update',
        cancelText: 'Cancel',
        onOk: () => {
          window.location.reload();
        },
      });
    }
  }
};

onMounted(() => {
  clearInterval(timer.value);
  timer.value = setInterval(async () => {
    checkUpdate();
  }, 30000);
});

Alternatively, use the community plugin plugin-web-update-notification :

// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { webUpdateNotice } from '@plugin-web-update-notification/vite';

export default defineConfig({
  plugins: [
    vue(),
    webUpdateNotice({
      logVersion: true,
    }),
  ]
});

These approaches enable automatic detection of new deployments and prompt users to refresh, eliminating stale pages without manual instructions.

FrontendCacheVueWebSocketviteSSEETag
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.