nuxt
Version:
[](https://nuxt.com)
121 lines (120 loc) • 4.25 kB
JavaScript
import { Suspense, Transition, computed, defineComponent, h, inject, nextTick, onMounted, provide, reactive, ref } from "vue";
import { RouterView } from "#vue-router";
import { defu } from "defu";
import { generateRouteKey, wrapInKeepAlive } from "./utils.js";
import { useNuxtApp } from "#app/nuxt";
import { _wrapIf } from "#app/components/utils";
import { LayoutMetaSymbol } from "#app/components/layout";
import { appKeepalive as defaultKeepaliveConfig, appPageTransition as defaultPageTransition } from "#build/nuxt.config.mjs";
export default defineComponent({
name: "NuxtPage",
inheritAttrs: false,
props: {
name: {
type: String
},
transition: {
type: [Boolean, Object],
default: void 0
},
keepalive: {
type: [Boolean, Object],
default: void 0
},
route: {
type: Object
},
pageKey: {
type: [Function, String],
default: null
}
},
setup(props, { attrs, expose }) {
const nuxtApp = useNuxtApp();
const pageRef = ref();
expose({ pageRef });
const _layoutMeta = inject(LayoutMetaSymbol, null);
let vnode;
return () => {
return h(RouterView, { name: props.name, route: props.route, ...attrs }, {
default: (routeProps) => {
if (!routeProps.Component) {
return;
}
if (vnode && _layoutMeta && !_layoutMeta.isCurrent(routeProps.route)) {
return vnode;
}
const key = generateRouteKey(routeProps, props.pageKey);
const done = nuxtApp.deferHydration();
const hasTransition = !!(props.transition ?? routeProps.route.meta.pageTransition ?? defaultPageTransition);
const transitionProps = hasTransition && _mergeTransitionProps([
props.transition,
routeProps.route.meta.pageTransition,
defaultPageTransition,
{ onAfterLeave: () => {
nuxtApp.callHook("page:transition:finish", routeProps.Component);
} }
].filter(Boolean));
vnode = _wrapIf(
Transition,
hasTransition && transitionProps,
wrapInKeepAlive(
props.keepalive ?? routeProps.route.meta.keepalive ?? defaultKeepaliveConfig,
h(Suspense, {
suspensible: true,
onPending: () => nuxtApp.callHook("page:start", routeProps.Component),
onResolve: () => {
nextTick(() => nuxtApp.callHook("page:finish", routeProps.Component).finally(done));
}
}, { default: () => h(RouteProvider, { key, routeProps, pageKey: key, hasTransition, pageRef }) })
)
).default();
return vnode;
}
});
};
}
});
function _toArray(val) {
return Array.isArray(val) ? val : val ? [val] : [];
}
function _mergeTransitionProps(routeProps) {
const _props = routeProps.map((prop) => ({
...prop,
onAfterLeave: _toArray(prop.onAfterLeave)
}));
return defu(..._props);
}
const RouteProvider = defineComponent({
name: "RouteProvider",
// TODO: Type props
// eslint-disable-next-line vue/require-prop-types
props: ["routeProps", "pageKey", "hasTransition", "pageRef"],
setup(props) {
const previousKey = props.pageKey;
const previousRoute = props.routeProps.route;
const route = {};
for (const key in props.routeProps.route) {
route[key] = computed(() => previousKey === props.pageKey ? props.routeProps.route[key] : previousRoute[key]);
}
provide("_route", reactive(route));
let vnode;
if (process.dev && process.client && props.hasTransition) {
onMounted(() => {
nextTick(() => {
if (["#comment", "#text"].includes(vnode?.el?.nodeName)) {
const filename = (vnode?.type).__file;
console.warn(`[nuxt] \`${filename}\` does not have a single root node and will cause errors when navigating between routes.`);
}
});
});
}
return () => {
if (process.dev && process.client) {
vnode = h(props.routeProps.Component, { ref: props.pageRef });
return vnode;
}
return h(props.routeProps.Component, { ref: props.pageRef });
};
}
});