feat(nuxt): remove wrapper from client only components (#6165)

Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
Julien Huang 2022-08-02 17:05:02 +02:00 committed by GitHub
parent e61f58b0f5
commit 2cdaf8065c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 12 deletions

View File

@ -1,4 +1,4 @@
import { ref, onMounted, defineComponent, createElementBlock, h } from 'vue'
import { ref, onMounted, defineComponent, createElementBlock, h, Fragment } from 'vue'
export default defineComponent({
name: 'ClientOnly',
@ -19,18 +19,38 @@ export default defineComponent({
})
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({
name: 'ClientOnlyWrapper',
inheritAttrs: false,
setup (_props, { attrs, slots }) {
const mounted = ref(false)
onMounted(() => { mounted.value = true })
return () => {
if (mounted.value) {
return h(component, attrs, slots)
}
return h('div', { class: attrs.class, style: attrs.style })
}
...component,
setup (props, ctx) {
const mounted$ = ref(false)
onMounted(() => { mounted$.value = true })
return Promise.resolve(setup?.(props, ctx) || {})
.then((setupState) => {
return typeof setupState !== 'function'
? { ...setupState, mounted$ }
: () => {
return mounted$.value
// use Fragment to avoid oldChildren is null issue
? h(Fragment, null, [h(setupState(props, ctx), ctx.attrs)])
: h('div', ctx.attrs)
}
})
}
})
}

View File

@ -44,6 +44,9 @@ describe('pages', () => {
expect(html).toContain('Composable | template: auto imported from ~/components/template.ts')
// should import components
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 via suffix')

View 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>

View File

@ -16,6 +16,7 @@
<CustomComponent />
<component :is="`test${'-'.toString()}global`" />
<component :is="`with${'-'.toString()}suffix`" />
<ClientWrapped ref="clientRef" style="color: red;" class="client-only" />
</div>
</template>
@ -31,4 +32,9 @@ useHead({
const foo = useFoo()
const bar = useBar()
const clientRef = ref()
onMounted(() => {
clientRef.value.exposedFunc()
})
</script>