UNPKG

@tanstack/vue-router

Version:

Modern and scalable routing for Vue applications

117 lines 5.34 kB
import * as Vue from 'vue'; import { Outlet } from './Match'; import { ClientOnly } from './ClientOnly'; // If the load fails due to module not found, it may mean a new version of // the build was deployed and the user's browser is still using an old version. // If this happens, the old version in the user's browser would have an outdated // URL to the lazy module. // In that case, we want to attempt one window refresh to get the latest. function isModuleNotFoundError(error) { // chrome: "Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split" // firefox: "error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split" // safari: "Importing a module script failed." if (typeof error?.message !== 'string') return false; return (error.message.startsWith('Failed to fetch dynamically imported module') || error.message.startsWith('error loading dynamically imported module') || error.message.startsWith('Importing a module script failed')); } export function lazyRouteComponent(importer, exportName, ssr) { let loadPromise; let comp = null; let error = null; let attemptedReload = false; const load = () => { // If we're on the server and SSR is disabled for this component if (typeof document === 'undefined' && ssr?.() === false) { comp = (() => null); return Promise.resolve(comp); } // Use existing promise or create new one if (!loadPromise) { loadPromise = importer() .then((res) => { loadPromise = undefined; comp = res[exportName ?? 'default']; return comp; }) .catch((err) => { error = err; loadPromise = undefined; // If it's a module not found error, we'll try to handle it in the component if (isModuleNotFoundError(error)) { return null; } throw err; }); } return loadPromise; }; // Create a lazy component wrapper using defineComponent so it works in Vue SFC templates const lazyComp = Vue.defineComponent({ name: 'LazyRouteComponent', setup(props) { // Create refs to track component state // Use shallowRef for component to avoid making it reactive (Vue warning) const component = Vue.shallowRef(comp ? Vue.markRaw(comp) : comp); const errorState = Vue.ref(error); const loading = Vue.ref(!component.value && !errorState.value); // Setup effect to load the component when this component is used Vue.onMounted(() => { if (!component.value && !errorState.value) { loading.value = true; load() .then((result) => { // Use markRaw to prevent Vue from making the component reactive component.value = result ? Vue.markRaw(result) : result; loading.value = false; }) .catch((err) => { errorState.value = err; loading.value = false; }); } }); // Handle module not found error with reload attempt if (errorState.value && isModuleNotFoundError(errorState.value) && !attemptedReload) { if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') { // Try to reload once on module not found error const storageKey = `tanstack_router_reload:${errorState.value.message}`; if (!sessionStorage.getItem(storageKey)) { sessionStorage.setItem(storageKey, '1'); attemptedReload = true; window.location.reload(); return () => null; // Return empty while reloading } } } // If we have a non-module-not-found error, throw it if (errorState.value && !isModuleNotFoundError(errorState.value)) { throw errorState.value; } // Return a render function return () => { // If we're still loading or don't have a component yet, use a suspense pattern if (loading.value || !component.value) { return Vue.h('div', null); // Empty div while loading } // If SSR is disabled for this component if (ssr?.() === false) { return Vue.h(ClientOnly, { fallback: Vue.h(Outlet), }, { default: () => Vue.h(component.value, props), }); } // Regular render with the loaded component return Vue.h(component.value, props); }; }, }); lazyComp.preload = load; return lazyComp; } //# sourceMappingURL=lazyRouteComponent.jsx.map