1
0
mirror of https://github.com/PanJiaChen/vue-element-admin.git synced 2026-06-14 00:59:13 +08:00

fix: recover from chunk load router errors

This commit is contained in:
cerredz 2026-05-15 22:58:35 -04:00
parent 6858a9ad67
commit 9444f9db28
3 changed files with 119 additions and 0 deletions

View File

@ -0,0 +1,66 @@
const chunkLoadErrorReloadKey = 'vue-element-admin:chunk-load-error-reload'
const chunkLoadErrorRetryWindow = 10 * 1000
export function isChunkLoadError(error) {
if (!error) {
return false
}
const name = error.name || ''
const message = error.message || String(error)
return name === 'ChunkLoadError' ||
/Loading (CSS )?chunk [\w-]+ failed/i.test(message) ||
/ChunkLoadError/i.test(message)
}
export function shouldReloadForChunkLoadError({
href = window.location.href,
storage = window.sessionStorage,
now = Date.now()
} = {}) {
if (!storage) {
return true
}
let previousReload
try {
previousReload = JSON.parse(storage.getItem(chunkLoadErrorReloadKey) || 'null')
} catch (error) {
previousReload = null
}
if (
previousReload &&
previousReload.href === href &&
now - previousReload.time < chunkLoadErrorRetryWindow
) {
return false
}
try {
storage.setItem(chunkLoadErrorReloadKey, JSON.stringify({ href, time: now }))
} catch (error) {
// Ignore storage failures; a single reload is still the best recovery path.
}
return true
}
export function handleChunkLoadError(error, {
href = window.location.href,
storage = window.sessionStorage,
reload = window.location.replace.bind(window.location),
now = Date.now()
} = {}) {
if (!isChunkLoadError(error)) {
return false
}
if (shouldReloadForChunkLoadError({ href, storage, now })) {
reload(href)
}
return true
}

View File

@ -1,5 +1,6 @@
import Vue from 'vue'
import Router from 'vue-router'
import { handleChunkLoadError } from './chunk-load-error'
Vue.use(Router)
@ -395,6 +396,10 @@ const createRouter = () => new Router({
const router = createRouter()
router.onError(error => {
handleChunkLoadError(error)
})
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()

View File

@ -0,0 +1,48 @@
import {
handleChunkLoadError,
isChunkLoadError,
shouldReloadForChunkLoadError
} from '@/router/chunk-load-error'
describe('router chunk-load error handling', () => {
test('detects Webpack chunk load failures', () => {
expect(isChunkLoadError(new Error('Loading chunk 12 failed.'))).toBe(true)
expect(isChunkLoadError(new Error('Loading CSS chunk app failed.'))).toBe(true)
expect(isChunkLoadError({ name: 'ChunkLoadError', message: 'missing' })).toBe(true)
expect(isChunkLoadError(new Error('NavigationDuplicated'))).toBe(false)
})
test('allows one reload for the same url in the retry window', () => {
const storage = window.sessionStorage
const href = 'http://localhost/#/dashboard'
storage.clear()
expect(shouldReloadForChunkLoadError({ href, storage, now: 1000 })).toBe(true)
expect(shouldReloadForChunkLoadError({ href, storage, now: 2000 })).toBe(false)
expect(shouldReloadForChunkLoadError({ href, storage, now: 12000 })).toBe(true)
})
test('reloads the current page for a chunk load failure', () => {
const reload = jest.fn()
const storage = window.sessionStorage
const href = 'http://localhost/#/permission/page'
storage.clear()
expect(handleChunkLoadError(new Error('Loading chunk 1 failed.'), {
href,
storage,
reload,
now: 1000
})).toBe(true)
expect(reload).toHaveBeenCalledWith(href)
})
test('ignores unrelated router errors', () => {
const reload = jest.fn()
expect(handleChunkLoadError(new Error('NavigationDuplicated'), { reload })).toBe(false)
expect(reload).not.toHaveBeenCalled()
})
})