UNPKG

nuxt

Version:

Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js.

154 lines (153 loc) 6 kB
import { Fragment, Suspense, Transition, defineComponent, h, inject, nextTick, ref, watch } from "vue"; import { RouterView } from "vue-router"; import { defu } from "defu"; import { generateRouteKey, toArray, wrapInKeepAlive } from "./utils.js"; import { RouteProvider } from "#app/components/route-provider"; import { useNuxtApp } from "#app/nuxt"; import { useRouter } from "#app/composables/router"; import { _wrapIf } from "#app/components/utils"; import { LayoutMetaSymbol, PageRouteSymbol } from "#app/components/injections"; 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, slots, expose }) { const nuxtApp = useNuxtApp(); const pageRef = ref(); const forkRoute = inject(PageRouteSymbol, null); let previousPageKey; expose({ pageRef }); const _layoutMeta = inject(LayoutMetaSymbol, null); let vnode; const done = nuxtApp.deferHydration(); if (import.meta.client && nuxtApp.isHydrating) { const removeErrorHook = nuxtApp.hooks.hookOnce("app:error", done); useRouter().beforeEach(removeErrorHook); } if (props.pageKey) { watch(() => props.pageKey, (next, prev) => { if (next !== prev) { nuxtApp.callHook("page:loading:start"); } }); } if (import.meta.dev) { nuxtApp._isNuxtPageUsed = true; } return () => { return h(RouterView, { name: props.name, route: props.route, ...attrs }, { default: (routeProps) => { const isRenderingNewRouteInOldFork = import.meta.client && haveParentRoutesRendered(forkRoute, routeProps.route, routeProps.Component); const hasSameChildren = import.meta.client && forkRoute && forkRoute.matched.length === routeProps.route.matched.length; if (!routeProps.Component) { if (import.meta.client && vnode && !hasSameChildren) { return vnode; } done(); return; } if (import.meta.client && vnode && _layoutMeta && !_layoutMeta.isCurrent(routeProps.route)) { return vnode; } if (import.meta.client && isRenderingNewRouteInOldFork && forkRoute && (!_layoutMeta || _layoutMeta?.isCurrent(forkRoute))) { if (hasSameChildren) { return vnode; } return null; } const key = generateRouteKey(routeProps, props.pageKey); if (!nuxtApp.isHydrating && !hasChildrenRoutes(forkRoute, routeProps.route, routeProps.Component) && previousPageKey === key) { nuxtApp.callHook("page:loading:end"); } previousPageKey = key; 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)); const keepaliveConfig = props.keepalive ?? routeProps.route.meta.keepalive ?? defaultKeepaliveConfig; vnode = _wrapIf( Transition, hasTransition && transitionProps, wrapInKeepAlive( keepaliveConfig, h(Suspense, { suspensible: true, onPending: () => nuxtApp.callHook("page:start", routeProps.Component), onResolve: () => { nextTick(() => nuxtApp.callHook("page:finish", routeProps.Component).then(() => nuxtApp.callHook("page:loading:end")).finally(done)); } }, { default: () => { const providerVNode = h(RouteProvider, { key: key || void 0, vnode: slots.default ? h(Fragment, void 0, slots.default(routeProps)) : routeProps.Component, route: routeProps.route, renderKey: key || void 0, trackRootNodes: hasTransition, vnodeRef: pageRef }); if (import.meta.client && keepaliveConfig) { providerVNode.type.name = routeProps.Component.type.name || routeProps.Component.type.__name || "RouteProvider"; } return providerVNode; } }) ) ).default(); return vnode; } }); }; } }); function _mergeTransitionProps(routeProps) { const _props = routeProps.map((prop) => ({ ...prop, onAfterLeave: prop.onAfterLeave ? toArray(prop.onAfterLeave) : void 0 })); return defu(..._props); } function haveParentRoutesRendered(fork, newRoute, Component) { if (!fork) { return false; } const index = newRoute.matched.findIndex((m) => m.components?.default === Component?.type); if (!index || index === -1) { return false; } return newRoute.matched.slice(0, index).some( (c, i) => c.components?.default !== fork.matched[i]?.components?.default ) || Component && generateRouteKey({ route: newRoute, Component }) !== generateRouteKey({ route: fork, Component }); } function hasChildrenRoutes(fork, newRoute, Component) { if (!fork) { return false; } const index = newRoute.matched.findIndex((m) => m.components?.default === Component?.type); return index < newRoute.matched.length - 1; }