fix(deploy): 前端资源 - 保留旧版本分片并恢复加载

This commit is contained in:
ddCat
2026-09-24 00:04:55 +08:00
parent 117fa7d70b
commit 69b9f02937
8 changed files with 137 additions and 6 deletions

View File

@@ -0,0 +1,43 @@
const RECOVERY_QUERY = "__chunk_recovery";
const RECOVERY_KEY = "canvas:chunk-recovery";
let recovering = false;
export function installChunkRecovery() {
window.addEventListener("vite:preloadError", handlePreloadError);
}
export function markChunkRecoverySucceeded() {
// An entry module loading says nothing about lazy routes. Keep the guard for
// the tab's lifetime; the URL also protects browsers with disabled storage.
}
/** Explicit user retry is allowed even after the automatic recovery was used. */
export function reloadAfterChunkFailure() {
navigateToFreshEntry();
}
export function tryRecoverChunkFailure() {
if (recovering) return true;
const url = new URL(window.location.href);
if (url.searchParams.has(RECOVERY_QUERY)) return false;
try {
if (sessionStorage.getItem(RECOVERY_KEY) === "1") return false;
sessionStorage.setItem(RECOVERY_KEY, "1");
} catch {
// The query marker survives the reload when session storage is denied.
}
recovering = true;
navigateToFreshEntry();
return true;
}
function navigateToFreshEntry() {
const url = new URL(window.location.href);
url.searchParams.set(RECOVERY_QUERY, String(Date.now()));
window.location.replace(url.toString());
}
function handlePreloadError(event: Event) {
// On the second failure let Vite throw to the route error boundary.
if (tryRecoverChunkFailure()) event.preventDefault();
}

View File

@@ -1,12 +1,15 @@
import "@fontsource-variable/inter";
import "@fontsource-variable/jetbrains-mono";
import { installChunkRecovery } from "@/lib/chunk-recovery";
import { bootstrapAppearance } from "@/services/appearance-bootstrap";
import { isIsolatedDirectorRepro } from "@/lib/dev-repro";
installChunkRecovery();
// The public film entry checks its availability independently of workspace bootstrap.
if (/^\/welcome\/?$/.test(window.location.pathname)) void import("./welcome-application");
else {
// The backend-free DEV lab must not make requests before AppProviders isolates it.
const appearanceReady = isIsolatedDirectorRepro(import.meta.env.DEV, window.location.pathname) ? Promise.resolve() : bootstrapAppearance();
void appearanceReady.finally(() => import("./application"));
void appearanceReady.then(() => import("./application"));
}

View File

@@ -3,6 +3,7 @@ import { Home, RefreshCw } from "lucide-react";
import { useNavigate, useRouteError } from "react-router";
import { WorkspaceSignalIcon } from "@/components/ui/aceternity/workspace-signal-icon";
import { reloadAfterChunkFailure } from "@/lib/chunk-recovery";
export default function RouteErrorPage() {
const error = useRouteError();
@@ -17,7 +18,7 @@ export default function RouteErrorPage() {
<h1 className="mt-3 text-2xl font-semibold">当前页面没有正常加载</h1>
<p className="mt-3 break-words text-sm leading-6 text-muted-foreground">{message}</p>
<div className="mt-6 flex justify-center gap-3">
<Button icon={<RefreshCw className="size-4" />} onClick={() => window.location.reload()}>重新加载</Button>
<Button icon={<RefreshCw className="size-4" />} onClick={reloadAfterChunkFailure}>重新加载</Button>
<Button type="primary" icon={<Home className="size-4" />} onClick={() => navigate("/")}>返回主页</Button>
</div>
</section>