fix(web): bound lazy route recovery

This commit is contained in:
lingniu
2026-07-16 05:29:08 +08:00
parent b21f9a60cd
commit d669531128
5 changed files with 97 additions and 5 deletions

View File

@@ -5,6 +5,10 @@ type RouteModule = { default: ComponentType };
type RoutePreloadConnection = { saveData?: boolean; effectiveType?: string };
type RoutePreloadRuntime = { visibilityState?: DocumentVisibilityState; deviceMemory?: number };
type RoutePreloadScheduler = (callback: () => void) => () => void;
type RouteLoadOptions = { timeoutMs?: number; retryDelayMs?: number; retries?: number };
const ROUTE_LOAD_TIMEOUT_MS = 10_000;
const ROUTE_RETRY_DELAY_MS = 180;
const importers: Record<RouteSection, () => Promise<RouteModule>> = {
monitor: () => import('../pages/MonitorPage'),
@@ -29,10 +33,50 @@ const likelyNextRoutes: Record<RouteSection, RouteSection[]> = {
operations: ['access', 'monitor']
};
function routeLoadTimeout<T>(promise: Promise<T>, section: string, timeoutMs: number) {
return new Promise<T>((resolve, reject) => {
const timer = window.setTimeout(() => {
const error = new Error(`Route chunk load timed out after ${timeoutMs}ms: ${section}`);
error.name = 'RouteChunkLoadTimeoutError';
reject(error);
}, timeoutMs);
promise.then(
(value) => { window.clearTimeout(timer); resolve(value); },
(error) => { window.clearTimeout(timer); reject(error); }
);
});
}
function retryDelay(delayMs: number) {
return new Promise<void>((resolve) => window.setTimeout(resolve, delayMs));
}
function isRouteLoadTimeout(error: unknown) {
return error instanceof Error && error.name === 'RouteChunkLoadTimeoutError';
}
export async function loadRouteModule<T>(
loader: () => Promise<T>,
section: string,
{ timeoutMs = ROUTE_LOAD_TIMEOUT_MS, retryDelayMs = ROUTE_RETRY_DELAY_MS, retries = 1 }: RouteLoadOptions = {}
) {
let lastError: unknown;
for (let attempt = 0; attempt <= retries; attempt += 1) {
try {
return await routeLoadTimeout(Promise.resolve().then(loader), section, timeoutMs);
} catch (error) {
lastError = error;
if (attempt >= retries || isRouteLoadTimeout(error)) break;
await retryDelay(retryDelayMs);
}
}
throw lastError;
}
function loadRoute(section: RouteSection) {
const pending = pendingModules.get(section);
if (pending) return pending;
const next = importers[section]();
const next = loadRouteModule(importers[section], section);
pendingModules.set(section, next);
void next.catch(() => pendingModules.delete(section));
return next;