fix(spa): self-heal stale clients after deploys (vite:preloadError + cache headers)
After a release the old hashed chunks are deleted, so an open tab from the previous build failed its next lazy-page import until a manual refresh. Three-part fix per the Vite deploy guide: - vite:preloadError listener reloads the page once (10s sessionStorage guard against loops) so stale tabs heal themselves - static cache headers: /assets/* immutable for a year (content-hashed names), index.html no-cache so new visits always see fresh chunk refs - missing /assets/* now 404 instead of getting the SPA index.html fallback (HTML-as-JS masked the failure) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
16
src/main.tsx
16
src/main.tsx
@@ -4,6 +4,22 @@ import { BrowserRouter } from "react-router-dom";
|
||||
import App from "./App";
|
||||
import { ThemeProvider } from "./context/ThemeContext";
|
||||
|
||||
// Deploy-skew recovery (official Vite mechanism): after a release the old
|
||||
// hashed chunks are deleted, so a tab still running the previous build fails
|
||||
// its next lazy-page import. Vite emits `vite:preloadError` for exactly this —
|
||||
// swallow the error and reload once, which loads the fresh index.html and new
|
||||
// chunk names. The timestamp guard prevents a reload loop when a chunk is
|
||||
// genuinely unloadable (e.g. offline): a second failure within 10 s falls
|
||||
// through to the normal error path.
|
||||
window.addEventListener("vite:preloadError", (event) => {
|
||||
const KEY = "vite-preload-reloaded-at";
|
||||
const last = Number(sessionStorage.getItem(KEY) || 0);
|
||||
if (Date.now() - last < 10_000) return;
|
||||
sessionStorage.setItem(KEY, String(Date.now()));
|
||||
event.preventDefault();
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter
|
||||
|
||||
Reference in New Issue
Block a user