mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-19 01:45:53 +00:00
fix(nuxt): remove div in client-only pages
This commit is contained in:
parent
c1b8b921f1
commit
29923890f9
@ -1,32 +1,73 @@
|
|||||||
import { defineAsyncComponent, defineComponent, h } from 'vue'
|
import { isPromise } from 'node:util/types'
|
||||||
import type { AsyncComponentLoader } from 'vue'
|
import { type AsyncComponentLoader, type ComponentOptions, h, onMounted, ref } from 'vue'
|
||||||
import ClientOnly from '#app/components/client-only'
|
import { useNuxtApp } from '#app'
|
||||||
import { useNuxtApp } from '#app/nuxt'
|
import ServerPlaceholder from '#app/components/server-placeholder'
|
||||||
|
|
||||||
/* @__NO_SIDE_EFFECTS__ */
|
/* @__NO_SIDE_EFFECTS__ */
|
||||||
export const createClientPage = (loader: AsyncComponentLoader) => {
|
export const createClientPage = (loader: AsyncComponentLoader) => {
|
||||||
const page = defineAsyncComponent(import.meta.dev
|
// Vue-router: Write "() => import('./MyPage.vue')" instead of "defineAsyncComponent(() => import('./MyPage.vue'))".
|
||||||
? () => loader().then((m) => {
|
return loader().then((m) => {
|
||||||
// mark component as client-only for `definePageMeta`
|
const c = m.default || m
|
||||||
(m.default || m).__clientOnlyPage = true
|
if (import.meta.dev) {
|
||||||
return m.default || m
|
// mark component as client-only for `definePageMeta`
|
||||||
})
|
c.__clientOnlyPage = true
|
||||||
: loader)
|
}
|
||||||
|
return pageToClientOnly(c)
|
||||||
return defineComponent({
|
|
||||||
inheritAttrs: false,
|
|
||||||
setup (_, { attrs }) {
|
|
||||||
const nuxtApp = useNuxtApp()
|
|
||||||
if (import.meta.server || nuxtApp.isHydrating) {
|
|
||||||
// wrapped with div to avoid Transition issues
|
|
||||||
// @see https://github.com/nuxt/nuxt/pull/25037#issuecomment-1877423894
|
|
||||||
return () => h('div', [
|
|
||||||
h(ClientOnly, undefined, {
|
|
||||||
default: () => h(page, attrs),
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
return () => h(page, attrs)
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cache = new WeakMap()
|
||||||
|
|
||||||
|
function pageToClientOnly<T extends ComponentOptions> (component: T) {
|
||||||
|
if (import.meta.server) {
|
||||||
|
return ServerPlaceholder
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache.has(component)) {
|
||||||
|
return cache.get(component)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clone = { ...component }
|
||||||
|
|
||||||
|
if (clone.render) {
|
||||||
|
// override the component render (non script setup component) or dev mode
|
||||||
|
clone.render = (ctx: any, cache: any, $props: any, $setup: any, $data: any, $options: any) => ($setup.mounted$ ?? ctx.mounted$)
|
||||||
|
? h(component.render?.bind(ctx)(ctx, cache, $props, $setup, $data, $options))
|
||||||
|
: h('div')
|
||||||
|
} else if (clone.template) {
|
||||||
|
// handle runtime-compiler template
|
||||||
|
clone.template = `
|
||||||
|
<template v-if="mounted$">${component.template}</template>
|
||||||
|
<template v-else><div></div></template>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
clone.setup = (props, ctx) => {
|
||||||
|
const nuxtApp = useNuxtApp()
|
||||||
|
const mounted$ = ref(nuxtApp.isHydrating === false)
|
||||||
|
onMounted(() => {
|
||||||
|
mounted$.value = true
|
||||||
|
})
|
||||||
|
const setupState = component.setup?.(props, ctx) || {}
|
||||||
|
if (isPromise(setupState)) {
|
||||||
|
return Promise.resolve(setupState).then((setupState: any) => {
|
||||||
|
if (typeof setupState !== 'function') {
|
||||||
|
setupState = setupState || {}
|
||||||
|
setupState.mounted$ = mounted$
|
||||||
|
return setupState
|
||||||
|
}
|
||||||
|
return (...args: any[]) => (mounted$.value || !nuxtApp.isHydrating) ? h(setupState(...args)) : h('div')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return typeof setupState === 'function'
|
||||||
|
? (...args: any[]) => (mounted$.value || !nuxtApp.isHydrating)
|
||||||
|
? h(setupState(...args))
|
||||||
|
: h('div')
|
||||||
|
: Object.assign(setupState, { mounted$ })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.set(component, clone)
|
||||||
|
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
|
||||||
// Disable page transition for this page to avoid having multiple time the same page during transition
|
|
||||||
pageTransition: false,
|
|
||||||
layoutTransition: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const state = useState('test', () => {
|
const state = useState('test', () => {
|
||||||
let hasAccessToWindow = null as null | boolean
|
let hasAccessToWindow = null as null | boolean
|
||||||
|
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const renderedOnServer = useState(() => import.meta.server)
|
const renderedOnServer = useState(() => import.meta.server)
|
||||||
definePageMeta({
|
|
||||||
// Disable page transition for this page to avoid having multiple time the same page during transition
|
|
||||||
pageTransition: false,
|
|
||||||
layoutTransition: false,
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
Loading…
Reference in New Issue
Block a user