fix: 发版后软刷新检测入口壳并在 chunk 404 时硬刷新

避免 Logo/下拉刷新仍跑旧 index.js,以及站内消息预取失败弹出英文 toast。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-01 07:53:39 +08:00
parent a02d103cfe
commit 37d14a22d5
7 changed files with 133 additions and 27 deletions

View File

@@ -2,6 +2,22 @@
const RELOAD_AT_KEY = 'j13:chunk-reload-at';
const RELOAD_COOLDOWN_MS = 15_000;
/** 从 HTML 中提取入口 module script(Vite 产物:/assets/index-*.js) */
function extractEntryScriptSrc(html: string): string | null {
const re = /<script[^>]*\stype=["']module["'][^>]*\ssrc=["']([^"']+)["'][^>]*>/i;
const m = html.match(re);
if (m?.[1]) return m[1];
// src 在 type 之前
const re2 = /<script[^>]*\ssrc=["']([^"']+)["'][^>]*\stype=["']module["'][^>]*>/i;
return html.match(re2)?.[1] ?? null;
}
/** 当前文档已加载的入口 script src */
export function getDocumentEntryScriptSrc(): string | null {
const el = document.querySelector<HTMLScriptElement>('script[type="module"][src]');
return el?.getAttribute('src') ?? null;
}
/** 判断是否为「发版后旧 JS chunk 失效」类错误 */
export function isChunkLoadError(error: unknown): boolean {
const msg = error instanceof Error
@@ -32,3 +48,29 @@ export function reloadForStaleChunk(): boolean {
window.location.reload();
return true;
}
/**
* 软刷新前对比入口 HTML 中的 index-*.js 哈希。
* 发版后当前页 chunk 仍在内存,裸 import() 不会重新拉文件,必须主动检测。
* @returns 是否已触发硬刷新(调用方应停止后续软刷新)
*/
export async function reloadIfShellStale(): Promise<boolean> {
const current = getDocumentEntryScriptSrc();
if (!current) return false;
try {
const res = await fetch(`${window.location.pathname}${window.location.search}`, {
cache: 'no-store',
headers: { Accept: 'text/html' },
credentials: 'same-origin',
});
if (!res.ok) return false;
const html = await res.text();
const next = extractEntryScriptSrc(html);
if (!next || next === current) return false;
return reloadForStaleChunk();
} catch {
// 网络失败:跳过,继续软刷新
return false;
}
}