mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
feat(nuxt): remove wrapper from client only components (#6165)
Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
parent
e61f58b0f5
commit
2cdaf8065c
@ -1,4 +1,4 @@
|
|||||||
import { ref, onMounted, defineComponent, createElementBlock, h } from 'vue'
|
import { ref, onMounted, defineComponent, createElementBlock, h, Fragment } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ClientOnly',
|
name: 'ClientOnly',
|
||||||
@ -19,18 +19,38 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export function createClientOnly (component) {
|
export function createClientOnly (component) {
|
||||||
|
const { setup, render: _render, template: _template } = component
|
||||||
|
if (_render) {
|
||||||
|
// override the component render (non <script setup> component)
|
||||||
|
component.render = (ctx, ...args) => {
|
||||||
|
return ctx.mounted$
|
||||||
|
? h(Fragment, null, [h(_render(ctx, ...args), ctx.$attrs ?? ctx._.attrs)])
|
||||||
|
: h('div', ctx.$attrs ?? ctx._.attrs)
|
||||||
|
}
|
||||||
|
} else if (_template) {
|
||||||
|
// handle runtime-compiler template
|
||||||
|
component.template = `
|
||||||
|
<template v-if="mounted$">${_template}</template>
|
||||||
|
<template v-else><div></div></template>
|
||||||
|
`
|
||||||
|
}
|
||||||
return defineComponent({
|
return defineComponent({
|
||||||
name: 'ClientOnlyWrapper',
|
...component,
|
||||||
inheritAttrs: false,
|
setup (props, ctx) {
|
||||||
setup (_props, { attrs, slots }) {
|
const mounted$ = ref(false)
|
||||||
const mounted = ref(false)
|
onMounted(() => { mounted$.value = true })
|
||||||
onMounted(() => { mounted.value = true })
|
|
||||||
return () => {
|
return Promise.resolve(setup?.(props, ctx) || {})
|
||||||
if (mounted.value) {
|
.then((setupState) => {
|
||||||
return h(component, attrs, slots)
|
return typeof setupState !== 'function'
|
||||||
}
|
? { ...setupState, mounted$ }
|
||||||
return h('div', { class: attrs.class, style: attrs.style })
|
: () => {
|
||||||
}
|
return mounted$.value
|
||||||
|
// use Fragment to avoid oldChildren is null issue
|
||||||
|
? h(Fragment, null, [h(setupState(props, ctx), ctx.attrs)])
|
||||||
|
: h('div', ctx.attrs)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,9 @@ describe('pages', () => {
|
|||||||
expect(html).toContain('Composable | template: auto imported from ~/components/template.ts')
|
expect(html).toContain('Composable | template: auto imported from ~/components/template.ts')
|
||||||
// should import components
|
// should import components
|
||||||
expect(html).toContain('This is a custom component with a named export.')
|
expect(html).toContain('This is a custom component with a named export.')
|
||||||
|
// should apply attributes to client-only components
|
||||||
|
expect(html).toContain('<div style="color:red;" class="client-only"></div>')
|
||||||
|
// should register global components automatically
|
||||||
expect(html).toContain('global component registered automatically')
|
expect(html).toContain('global component registered automatically')
|
||||||
expect(html).toContain('global component via suffix')
|
expect(html).toContain('global component via suffix')
|
||||||
|
|
||||||
|
17
test/fixtures/basic/components/ClientWrapped.client.vue
vendored
Normal file
17
test/fixtures/basic/components/ClientWrapped.client.vue
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
function exposedFunc () {
|
||||||
|
console.log('ok')
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ exposedFunc })
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 300))
|
||||||
|
|
||||||
|
onMounted(() => { console.log('mounted') })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
client-only component
|
||||||
|
</div>
|
||||||
|
</template>
|
6
test/fixtures/basic/pages/index.vue
vendored
6
test/fixtures/basic/pages/index.vue
vendored
@ -16,6 +16,7 @@
|
|||||||
<CustomComponent />
|
<CustomComponent />
|
||||||
<component :is="`test${'-'.toString()}global`" />
|
<component :is="`test${'-'.toString()}global`" />
|
||||||
<component :is="`with${'-'.toString()}suffix`" />
|
<component :is="`with${'-'.toString()}suffix`" />
|
||||||
|
<ClientWrapped ref="clientRef" style="color: red;" class="client-only" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -31,4 +32,9 @@ useHead({
|
|||||||
|
|
||||||
const foo = useFoo()
|
const foo = useFoo()
|
||||||
const bar = useBar()
|
const bar = useBar()
|
||||||
|
const clientRef = ref()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
clientRef.value.exposedFunc()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user