52 lines
2.4 KiB
TypeScript
52 lines
2.4 KiB
TypeScript
export default defineNuxtPlugin((nuxtApp) => {
|
|
// 监听并拦截异步 Chunk / 静态资源加载失败的问题(通常是因为服务端重新部署后,旧缓存还在尝试请求被删除的 hash 文件)
|
|
nuxtApp.hook('app:chunkError', ({ error }) => {
|
|
console.error('Nuxt Chunk/Asset Loading Failed. Triggering hard reload...', error)
|
|
|
|
// 防止无限刷新,使用 sessionStorage 记录
|
|
const reloadCount = parseInt(sessionStorage.getItem('chunk_reload_count') || '0')
|
|
if (reloadCount < 3) {
|
|
sessionStorage.setItem('chunk_reload_count', String(reloadCount + 1))
|
|
window.location.reload()
|
|
} else {
|
|
console.warn('Too many reload attempts for chunk error. Giving up.')
|
|
}
|
|
})
|
|
|
|
// 页面成功加载后清除刷新计数器
|
|
nuxtApp.hook('page:finish', () => {
|
|
sessionStorage.removeItem('chunk_reload_count')
|
|
})
|
|
|
|
// 拦截全局未捕获的 Promise 错误,防止它阻塞后续操作(例如因为插件拦截导致的 'A listener indicated an asynchronous response...' 错误)
|
|
if (import.meta.client) {
|
|
window.addEventListener('unhandledrejection', (event) => {
|
|
const err = event.reason?.message || event.reason?.toString() || ''
|
|
if (err.includes('message channel closed') || err.includes('A listener indicated')) {
|
|
// 这是常见的浏览器插件注入引起的无害错误,直接忽略
|
|
event.preventDefault()
|
|
}
|
|
})
|
|
|
|
// 监听全局 Error 事件,捕获 CSS 等资源的加载失败
|
|
window.addEventListener('error', (event) => {
|
|
const target = event.target as HTMLElement
|
|
// 检查是否是 link(css) 或 script 的加载错误
|
|
if (target && (target.tagName === 'LINK' || target.tagName === 'SCRIPT')) {
|
|
const href = (target as HTMLLinkElement).href || (target as HTMLScriptElement).src
|
|
if (href && href.includes('/_nuxt/')) {
|
|
console.warn('Nuxt static asset failed to load:', href)
|
|
const reloadCount = parseInt(sessionStorage.getItem('asset_reload_count') || '0')
|
|
if (reloadCount < 2) {
|
|
sessionStorage.setItem('asset_reload_count', String(reloadCount + 1))
|
|
// 延迟一点刷新以防频繁请求
|
|
setTimeout(() => {
|
|
window.location.reload()
|
|
}, 1000)
|
|
}
|
|
}
|
|
}
|
|
}, true) // true = 捕获阶段,以便拦截资源加载错误
|
|
}
|
|
})
|