feat(nuxt3): make layout and other page meta reactive (#2926)

This commit is contained in:
Daniel Roe 2022-01-26 11:56:24 +00:00 committed by GitHub
parent 696138794b
commit 944464781d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 19 deletions

View File

@ -79,12 +79,10 @@ You can also use a ref or computed property for your layout.
<script setup>
const route = useRoute()
function enableCustomLayout () {
// Note: because it's within a ref, it will persist if
// you navigate away and then back to the page.
route.meta.layout.value = "custom"
route.meta.layout = "custom"
}
definePageMeta({
layout: ref(false),
layout: false,
});
</script>
```

View File

@ -112,8 +112,27 @@ export default defineNuxtModule({
}
})
addTemplate({
filename: 'layouts.d.ts',
write: true,
getContents: async () => {
const composablesFile = resolve(runtimeDir, 'composables')
const layouts = await resolveLayouts(nuxt)
return [
'import { ComputedRef, Ref } from \'vue\'',
`export type LayoutKey = ${layouts.map(layout => `"${layout.name}"`).join(' | ') || 'string'}`,
`declare module '${composablesFile}' {`,
' interface PageMeta {',
' layout?: false | LayoutKey | Ref<LayoutKey> | ComputedRef<LayoutKey>',
' }',
'}'
].join('\n')
}
})
nuxt.hook('prepare:types', ({ references }) => {
references.push({ path: resolve(nuxt.options.buildDir, 'middleware.d.ts') })
references.push({ path: resolve(nuxt.options.buildDir, 'layouts.d.ts') })
})
// Add layouts template

View File

@ -1,4 +1,4 @@
import { ComputedRef, KeepAliveProps, Ref, TransitionProps } from 'vue'
import { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw } from 'vue-router'
import { useNuxtApp } from '#app'
@ -14,13 +14,12 @@ export interface PageMeta {
[key: string]: any
pageTransition?: false | TransitionProps
layoutTransition?: false | TransitionProps
layout?: false | string | Ref<false | string> | ComputedRef<false | string>
key?: string | ((route: RouteLocationNormalizedLoaded) => string)
keepalive?: false | KeepAliveProps
}
declare module 'vue-router' {
interface RouteMeta extends PageMeta {}
interface RouteMeta extends UnwrapRef<PageMeta> {}
}
const warnRuntimeUsage = (method: string) =>

View File

@ -25,26 +25,34 @@ export default defineComponent({
const hasLayout = props.layout ?? route.meta.layout ?? 'default' in layouts
return h(RouterView, {}, {
default: ({ Component }: RouterViewSlotProps) => Component && wrapIf(Transition, hasLayout && (route.meta.layoutTransition ?? defaultLayoutTransition), {
default: () => wrapIf(NuxtLayout, hasLayout && { layout: props.layout ?? route.meta.layout }, {
default: () => wrapIf(Transition, route.meta.pageTransition ?? defaultPageTransition, {
default: () => wrapIf(KeepAlive, process.client && route.meta.keepalive, h(Suspense, {
default: ({ Component }: RouterViewSlotProps) => Component &&
wrapIf(Transition, hasLayout && (route.meta.layoutTransition ?? defaultLayoutTransition),
wrapIf(NuxtLayout, hasLayout && { name: props.layout ?? route.meta.layout },
wrapIf(Transition, route.meta.pageTransition ?? defaultPageTransition,
wrapInKeepAlive(route.meta.keepalive, h(Suspense, {
onPending: () => nuxtApp.callHook('page:start', Component),
onResolve: () => nuxtApp.callHook('page:finish', Component)
}, { default: () => h(Component) }))
})
})
})
}, { default: () => h(Component) })
)
)
)).default()
})
}
}
})
const wrapIf = (component: Component, props: any, slotsOrChildren: any) => {
if (props) {
return h(component, props === true ? {} : props, slotsOrChildren)
const Fragment = {
setup (props, { slots }) {
return () => slots.default()
}
return slotsOrChildren.default?.() || slotsOrChildren
}
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' }

View File

@ -66,6 +66,7 @@ export default defineNuxtPlugin((nuxtApp) => {
}
router.beforeEach(async (to, from) => {
to.meta = reactive(to.meta)
nuxtApp._processingMiddleware = true
type MiddlewareDef = string | NavigationGuard