30 lines
1.4 KiB
JavaScript
30 lines
1.4 KiB
JavaScript
/**
|
|
* retryableAsyncComponent.js — SHARED (@map/composables/retryableAsyncComponent.js).
|
|
*
|
|
* ★ WHY (card BUG-LAZY-PLUGIN-DEATH-ON-A-SINGLE-FAILED-IMPORT). Vue's defineAsyncComponent
|
|
* caches a REJECTED loader promise: once a dynamic import() fails, that component is dead for
|
|
* the life of the page — every later render replays the same rejection. The agent hit exactly
|
|
* this: one net::ERR_NETWORK_CHANGED event (wifi switch / VPN / sleep-wake, a TRANSPORT blip,
|
|
* not a 404) failed five unrelated module fetches at the same moment, and doxa-simple-map +
|
|
* doxa-research-map stayed "not loading" until a full reload — a working product reading as
|
|
* two broken plugins.
|
|
*
|
|
* This is the ONE shared wrapper every lazy component goes through instead. It uses
|
|
* defineAsyncComponent's own onError contract — retry() re-invokes the loader, which issues a
|
|
* FRESH import() request — with 3 attempts and a short growing backoff, which is precisely the
|
|
* shape a transient network blip needs. If all attempts fail (a real 404, a dead server), it
|
|
* fails for good: retrying forever would hide genuine breakage.
|
|
*
|
|
* Usage (drop-in): retryableAsyncComponent(() => import('./Heavy.vue'))
|
|
*/
|
|
import { defineAsyncComponent } from 'vue'
|
|
|
|
export function retryableAsyncComponent(loader) {
|
|
return defineAsyncComponent({
|
|
loader,
|
|
onError(error, retry, fail, attempts) {
|
|
if (attempts <= 3) setTimeout(retry, attempts * 500)
|
|
else fail()
|
|
},
|
|
})
|
|
}
|