refactor: extract <NuxtLayout> from <NuxtPage> (#3011)

This commit is contained in:
Daniel Roe 2022-01-31 18:58:19 +00:00 committed by GitHub
parent 47dfb7b519
commit 083f90b719
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 49 deletions

View File

@ -25,7 +25,9 @@ If you have a [`pages/`](/docs/directory-structure/pages) directory, to display
```vue [app.vue] ```vue [app.vue]
<template> <template>
<div> <div>
<NuxtPage/> <NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</div> </div>
</template> </template>
``` ```

View File

@ -1,3 +1,5 @@
<template> <template>
<NuxtPage /> <NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template> </template>

View File

@ -1,24 +1,33 @@
import { defineComponent, h, Ref } from 'vue' import { defineComponent, isRef, Ref, Transition } from 'vue'
import { useRoute } from 'vue-router'
import { wrapIf } from './utils'
// @ts-ignore // @ts-ignore
import layouts from '#build/layouts' import layouts from '#build/layouts'
const defaultLayoutTransition = { name: 'layout', mode: 'out-in' }
export default defineComponent({ export default defineComponent({
props: { props: {
name: { name: {
type: [String, Boolean, Object] as unknown as () => string | false | Ref<string | false>, type: [String, Boolean, Object] as unknown as () => string | false | Ref<string | false>,
default: 'default' default: null
} }
}, },
setup (props, context) { setup (props, context) {
const route = useRoute()
return () => { return () => {
const layout = (props.name && typeof props.name === 'object' ? props.name.value : props.name) ?? 'default' const layout = (isRef(props.name) ? props.name.value : props.name) ?? route.meta.layout as string ?? 'default'
if (!layouts[layout]) {
if (process.dev && layout && layout !== 'default') { const hasLayout = layout && layout in layouts
console.warn(`Invalid layout \`${layout}\` selected.`) if (process.dev && layout && !hasLayout && layout !== 'default') {
} console.warn(`Invalid layout \`${layout}\` selected.`)
return context.slots.default()
} }
return h(layouts[layout], props, context.slots)
// We avoid rendering layout transition if there is no layout to render
return wrapIf(Transition, hasLayout && (route.meta.layoutTransition ?? defaultLayoutTransition),
wrapIf(layouts[layout], hasLayout, context.slots)
).default()
} }
} }
}) })

View File

@ -1,59 +1,27 @@
import { Component, defineComponent, KeepAlive, h, Suspense, Transition } from 'vue' import { defineComponent, h, Suspense, Transition } from 'vue'
import { RouterView, useRoute } from 'vue-router' import { RouterView } from 'vue-router'
import NuxtLayout from './layout' import { wrapIf, wrapInKeepAlive } from './utils'
import { useNuxtApp } from '#app' import { useNuxtApp } from '#app'
// @ts-ignore
import layouts from '#build/layouts'
type InstanceOf<T> = T extends new (...args: any[]) => infer R ? R : never type InstanceOf<T> = T extends new (...args: any[]) => infer R ? R : never
type RouterViewSlotProps = Parameters<InstanceOf<typeof RouterView>['$slots']['default']>[0] type RouterViewSlotProps = Parameters<InstanceOf<typeof RouterView>['$slots']['default']>[0]
export default defineComponent({ export default defineComponent({
name: 'NuxtPage', name: 'NuxtPage',
props: { setup () {
layout: {
type: String,
default: null
}
},
setup (props) {
const nuxtApp = useNuxtApp() const nuxtApp = useNuxtApp()
const route = useRoute()
return () => { return () => {
// We avoid rendering layout transition if there is no layout to render
const hasLayout = props.layout ?? route.meta.layout ?? 'default' in layouts
return h(RouterView, {}, { return h(RouterView, {}, {
default: ({ Component }: RouterViewSlotProps) => Component && default: ({ Component, route }: RouterViewSlotProps) => Component &&
wrapIf(Transition, hasLayout && (route.meta.layoutTransition ?? defaultLayoutTransition),
wrapIf(NuxtLayout, hasLayout && { name: props.layout ?? route.meta.layout },
wrapIf(Transition, route.meta.pageTransition ?? defaultPageTransition, wrapIf(Transition, route.meta.pageTransition ?? defaultPageTransition,
wrapInKeepAlive(route.meta.keepalive, h(Suspense, { wrapInKeepAlive(route.meta.keepalive, h(Suspense, {
onPending: () => nuxtApp.callHook('page:start', Component), onPending: () => nuxtApp.callHook('page:start', Component),
onResolve: () => nuxtApp.callHook('page:finish', Component) onResolve: () => nuxtApp.callHook('page:finish', Component)
}, { default: () => h(Component) }) }, { default: () => h(Component) }))).default()
)
)
)).default()
}) })
} }
} }
}) })
const Fragment = {
setup (props, { slots }) {
return () => slots.default()
}
}
const wrapIf = (component: Component, props: any, slots: any) => {
return { default: () => props ? h(component, props === true ? {} : props, slots) : h(Fragment, {}, slots) }
}
const wrapInKeepAlive = (props: any, children: any) => {
return { default: () => process.client && props ? h(KeepAlive, props === true ? {} : props, children) : children }
}
const defaultLayoutTransition = { name: 'layout', mode: 'out-in' }
const defaultPageTransition = { name: 'page', mode: 'out-in' } const defaultPageTransition = { name: 'page', mode: 'out-in' }

View File

@ -0,0 +1,15 @@
import { Component, KeepAlive, h } from 'vue'
const Fragment = {
setup (_props, { slots }) {
return () => slots.default()
}
}
export const wrapIf = (component: Component, props: any, slots: any) => {
return { default: () => props ? h(component, props === true ? {} : props, slots) : h(Fragment, {}, slots) }
}
export const wrapInKeepAlive = (props: any, children: any) => {
return { default: () => process.client && props ? h(KeepAlive, props === true ? {} : props, children) : children }
}