fix: 部署后 chunk 404 自动刷新,SPA index 禁止缓存
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m37s

前端检测 lazy import 失败自动 reload;nginx @spa 回退统一 no-cache。
This commit is contained in:
zzh 2026-09-09 16:33:07 +08:00
parent bdb515d366
commit 8f12a6cf65
3 changed files with 134 additions and 78 deletions

View File

@ -55,109 +55,118 @@ server {
}
location = /login {
try_files /index.html =200;
try_files $uri @spa;
}
location = /login/ {
try_files /index.html =200;
try_files $uri @spa;
}
rewrite ^/admin/?$ /index.html last;
location = /admin {
try_files /index.html =200;
try_files $uri @spa;
}
location = /admin/ {
try_files /index.html =200;
try_files $uri @spa;
}
location ~ ^/api/(upstreamInterface|upstreamInterfaceConfig|upstreamInterfaceConfigEdit|downstreamInterface(|/newPort)|appList(|/createApplication)|requestLog|callStatistics|apiDebugger|apiTest)(/|$) {
try_files /index.html =200;
try_files $uri @spa;
}
location ^~ /xhs/startService { try_files /index.html =200; }
location ^~ /xhs/creation { try_files /index.html =200; }
location ^~ /xhs/publishHistory { try_files /index.html =200; }
location ^~ /xhs/skills { try_files /index.html =200; }
location ^~ /xhs/humanSettings { try_files /index.html =200; }
location ^~ /xhs/agentPair { try_files /index.html =200; }
location = /xhs/analysis { try_files /index.html =200; }
location = /xhs/analysis/ { try_files /index.html =200; }
location ^~ /xhs/startService { try_files $uri @spa; }
location ^~ /xhs/creation { try_files $uri @spa; }
location ^~ /xhs/publishHistory { try_files $uri @spa; }
location ^~ /xhs/skills { try_files $uri @spa; }
location ^~ /xhs/humanSettings { try_files $uri @spa; }
location ^~ /xhs/agentPair { try_files $uri @spa; }
location = /xhs/analysis { try_files $uri @spa; }
location = /xhs/analysis/ { try_files $uri @spa; }
location ^~ /wechat/register { try_files /index.html =200; }
location ^~ /wechat/bind { try_files /index.html =200; }
location ^~ /h5 { try_files /index.html =200; }
location ^~ /track/query { try_files /index.html =200; }
location ^~ /operation/inboundScan { try_files /index.html =200; }
location ^~ /operation/outboundScan { try_files /index.html =200; }
location ^~ /operation/printLabel { try_files /index.html =200; }
location ^~ /operation/parcelArrival { try_files /index.html =200; }
location ^~ /operation/fulfillPack { try_files /index.html =200; }
location ^~ /wechat/register { try_files $uri @spa; }
location ^~ /wechat/bind { try_files $uri @spa; }
location ^~ /h5 { try_files $uri @spa; }
location ^~ /track/query { try_files $uri @spa; }
location ^~ /operation/inboundScan { try_files $uri @spa; }
location ^~ /operation/outboundScan { try_files $uri @spa; }
location ^~ /operation/printLabel { try_files $uri @spa; }
location ^~ /operation/parcelArrival { try_files $uri @spa; }
location ^~ /operation/fulfillPack { try_files $uri @spa; }
# 订单前端路由(^~ 须比下方 /order/ API 正则代理更优先)
location ^~ /order/priceCheck { try_files /index.html =200; }
location ^~ /order/placeOrder { try_files /index.html =200; }
location ^~ /order/myOrders { try_files /index.html =200; }
location ^~ /order/fulfillOrders { try_files /index.html =200; }
location ^~ /order/waybillManage { try_files /index.html =200; }
location ^~ /order/pickupOrder { try_files /index.html =200; }
location ^~ /order/customs9610Order { try_files /index.html =200; }
location ^~ /order/batchJobs { try_files /index.html =200; }
location ^~ /order/templateMapping { try_files /index.html =200; }
location ^~ /order/platformStore { try_files /index.html =200; }
location ^~ /order/platformStoreOrder { try_files /index.html =200; }
location ^~ /order/priceCheck { try_files $uri @spa; }
location ^~ /order/placeOrder { try_files $uri @spa; }
location ^~ /order/myOrders { try_files $uri @spa; }
location ^~ /order/fulfillOrders { try_files $uri @spa; }
location ^~ /order/waybillManage { try_files $uri @spa; }
location ^~ /order/pickupOrder { try_files $uri @spa; }
location ^~ /order/customs9610Order { try_files $uri @spa; }
location ^~ /order/batchJobs { try_files $uri @spa; }
location ^~ /order/templateMapping { try_files $uri @spa; }
location ^~ /order/platformStore { try_files $uri @spa; }
location ^~ /order/platformStoreOrder { try_files $uri @spa; }
# 知识库前端路由(^~ 须比下方 /kb/ API 代理更优先)
location ^~ /kb/category { try_files /index.html =200; }
location ^~ /kb/article { try_files /index.html =200; }
location ^~ /kb/category { try_files $uri @spa; }
location ^~ /kb/article { try_files $uri @spa; }
location = /warehouse/parcel { try_files /index.html =200; }
location = /warehouse/inventory { try_files /index.html =200; }
location = /warehouse/sku { try_files /index.html =200; }
location = /warehouse/binLocation { try_files /index.html =200; }
location ^~ /warehouse/inboundNotice { try_files /index.html =200; }
location ^~ /Plugin/ { try_files /index.html =200; }
location = /warehouse/parcel { try_files $uri @spa; }
location = /warehouse/inventory { try_files $uri @spa; }
location = /warehouse/sku { try_files $uri @spa; }
location = /warehouse/binLocation { try_files $uri @spa; }
location ^~ /warehouse/inboundNotice { try_files $uri @spa; }
location ^~ /Plugin/ { try_files $uri @spa; }
location ^~ /channel/salesProducts { try_files /index.html =200; }
location ^~ /channel/serviceChannels { try_files /index.html =200; }
location ^~ /channel/priceArea { try_files /index.html =200; }
location ^~ /channel/blacklist { try_files /index.html =200; }
location ^~ /channel/remoteZones { try_files /index.html =200; }
location ^~ /channel/weightRules { try_files /index.html =200; }
location ^~ /channel/weightRounding { try_files /index.html =200; }
location ^~ /channel/autoTrigger { try_files /index.html =200; }
location ^~ /channel/issueTemplate { try_files /index.html =200; }
location ^~ /channel/labelDesign { try_files /index.html =200; }
location ^~ /channel/salesProducts { try_files $uri @spa; }
location ^~ /channel/serviceChannels { try_files $uri @spa; }
location ^~ /channel/priceArea { try_files $uri @spa; }
location ^~ /channel/blacklist { try_files $uri @spa; }
location ^~ /channel/remoteZones { try_files $uri @spa; }
location ^~ /channel/weightRules { try_files $uri @spa; }
location ^~ /channel/weightRounding { try_files $uri @spa; }
location ^~ /channel/autoTrigger { try_files $uri @spa; }
location ^~ /channel/issueTemplate { try_files $uri @spa; }
location ^~ /channel/labelDesign { try_files $uri @spa; }
# 营销前端路由(^~ 须比下方 /marketing/ API 正则代理更优先)
location ^~ /marketing/memberLevel { try_files /index.html =200; }
location ^~ /marketing/pointsRule { try_files /index.html =200; }
location ^~ /marketing/pointsFlow { try_files /index.html =200; }
location ^~ /marketing/couponTemplate { try_files /index.html =200; }
location ^~ /marketing/couponGrant { try_files /index.html =200; }
location ^~ /marketing/commissionRule { try_files /index.html =200; }
location ^~ /marketing/commissionDetail { try_files /index.html =200; }
location ^~ /marketing/commissionWithdraw { try_files /index.html =200; }
location ^~ /marketing/referralTree { try_files /index.html =200; }
location ^~ /marketing/memberLevel { try_files $uri @spa; }
location ^~ /marketing/pointsRule { try_files $uri @spa; }
location ^~ /marketing/pointsFlow { try_files $uri @spa; }
location ^~ /marketing/couponTemplate { try_files $uri @spa; }
location ^~ /marketing/couponGrant { try_files $uri @spa; }
location ^~ /marketing/commissionRule { try_files $uri @spa; }
location ^~ /marketing/commissionDetail { try_files $uri @spa; }
location ^~ /marketing/commissionWithdraw { try_files $uri @spa; }
location ^~ /marketing/referralTree { try_files $uri @spa; }
# 系统管理前端路由(^~ 须比下方 /system/ API 正则代理更优先)
location = /system/dict { try_files /index.html =200; }
location = /system/warehouseAddress { try_files /index.html =200; }
location = /system/announcement { try_files /index.html =200; }
location ^~ /system/users { try_files /index.html =200; }
location ^~ /system/roles { try_files /index.html =200; }
location ^~ /system/menus { try_files /index.html =200; }
location ^~ /system/organizations { try_files /index.html =200; }
location ^~ /system/batchEmail { try_files /index.html =200; }
location ^~ /system/otherConfig { try_files /index.html =200; }
location ^~ /system/aiAssistant { try_files /index.html =200; }
location ^~ /system/aiSkills { try_files /index.html =200; }
location ^~ /system/mcpTools { try_files /index.html =200; }
location ^~ /system/dataPerm/ { try_files /index.html =200; }
location = /system/dict { try_files $uri @spa; }
location = /system/warehouseAddress { try_files $uri @spa; }
location = /system/announcement { try_files $uri @spa; }
location ^~ /system/users { try_files $uri @spa; }
location ^~ /system/roles { try_files $uri @spa; }
location ^~ /system/menus { try_files $uri @spa; }
location ^~ /system/organizations { try_files $uri @spa; }
location ^~ /system/batchEmail { try_files $uri @spa; }
location ^~ /system/otherConfig { try_files $uri @spa; }
location ^~ /system/aiAssistant { try_files $uri @spa; }
location ^~ /system/aiSkills { try_files $uri @spa; }
location ^~ /system/mcpTools { try_files $uri @spa; }
location ^~ /system/dataPerm/ { try_files $uri @spa; }
# SPA 回退:try_files $uri @spa 统一带 no-cache,避免部署后浏览器缓存旧 index.html
location @spa {
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
try_files /index.html =404;
}
location = /index.html {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
try_files /index.html =404;
}
# 微信 JS 安全域名校验:原样返回 txt,禁止 SPA 回落 index.html
@ -176,7 +185,6 @@ server {
}
location / {
index index.html;
try_files $uri /index.html;
try_files $uri @spa;
}
}

View File

@ -14,6 +14,7 @@ import {
} from './plugins/heavy-plugins';
import { initAuthKeepAlive } from '@/utils/auth-keepalive';
import { installWeChatH5HistoryGuard } from '@/utils/device';
import { installChunkReloadHandlers, reloadOnChunkLoadError } from '@/utils/chunkReload';
import { setAuthToken } from './api/rbac';
import { useRbacUserStore } from '@/store/modules/rbac-user';
import { applyRbacUserToUserStore } from '@/utils/enter-system';
@ -75,6 +76,11 @@ const app = createApp(App);
app.use(ArcoVue, {});
app.use(ArcoVueIcon);
app.use(router);
installChunkReloadHandlers();
router.onError((error) => {
if (reloadOnChunkLoadError(error)) return;
throw error;
});
app.use(store);
useAppStore().hydrateTheme();
app.use(i18n);

42
src/utils/chunkReload.ts Normal file
View File

@ -0,0 +1,42 @@
/** 部署后旧 chunk 404 时自动刷新,避免 lazy route 白屏 */
const RELOAD_KEY = 'oms_chunk_reload_ts';
const RELOAD_COOLDOWN_MS = 15_000;
export function isChunkLoadError(error: unknown): boolean {
const msg = error instanceof Error ? error.message : String(error ?? '');
return (
/Failed to fetch dynamically imported module/i.test(msg) ||
/Importing a module script failed/i.test(msg) ||
/Loading chunk [\da-zA-Z_-]+ failed/i.test(msg) ||
/ChunkLoadError/i.test(msg)
);
}
/** 检测到 chunk 加载失败则刷新页面;10s 内仅刷新一次,防止死循环 */
export function reloadOnChunkLoadError(error: unknown): boolean {
if (typeof window === 'undefined' || !isChunkLoadError(error)) {
return false;
}
try {
const last = Number(sessionStorage.getItem(RELOAD_KEY) || 0);
const now = Date.now();
if (now - last < RELOAD_COOLDOWN_MS) {
return false;
}
sessionStorage.setItem(RELOAD_KEY, String(now));
} catch {
/* ignore */
}
window.location.reload();
return true;
}
export function installChunkReloadHandlers(): void {
if (typeof window === 'undefined') return;
window.addEventListener('unhandledrejection', (event) => {
if (reloadOnChunkLoadError(event.reason)) {
event.preventDefault();
}
});
}