@tanstack/vue-router
Version:
Modern and scalable routing for Vue applications
117 lines • 5.34 kB
JSX
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