mirror of
https://github.com/PanJiaChen/vue-element-admin.git
synced 2026-07-12 19:51:15 +08:00
Merge 9444f9db28198900db0d03544200b2e683593c6c into 6858a9ad67483025f6a9432a926beb9327037be3
This commit is contained in:
commit
50cc6064f6
66
src/router/chunk-load-error.js
Normal file
66
src/router/chunk-load-error.js
Normal 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
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Router from 'vue-router'
|
import Router from 'vue-router'
|
||||||
|
import { handleChunkLoadError } from './chunk-load-error'
|
||||||
|
|
||||||
Vue.use(Router)
|
Vue.use(Router)
|
||||||
|
|
||||||
@ -395,6 +396,10 @@ const createRouter = () => new Router({
|
|||||||
|
|
||||||
const router = createRouter()
|
const router = createRouter()
|
||||||
|
|
||||||
|
router.onError(error => {
|
||||||
|
handleChunkLoadError(error)
|
||||||
|
})
|
||||||
|
|
||||||
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
|
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
|
||||||
export function resetRouter() {
|
export function resetRouter() {
|
||||||
const newRouter = createRouter()
|
const newRouter = createRouter()
|
||||||
|
|||||||
48
tests/unit/router/chunk-load-error.spec.js
Normal file
48
tests/unit/router/chunk-load-error.spec.js
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
x
Reference in New Issue
Block a user