fix(nuxt): import and wrap client-only components once (#7245)

This commit is contained in:
Anthony Fu 2022-09-06 15:40:25 +08:00 committed by GitHub
parent d115b01a35
commit 96b5b8fd6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 33 deletions

View File

@ -19,38 +19,39 @@ export default defineComponent({
}) })
export function createClientOnly (component) { export function createClientOnly (component) {
const { setup, render: _render, template: _template } = component const clone = { ...component }
if (_render) {
if (clone.render) {
// override the component render (non script setup component) // override the component render (non script setup component)
component.render = (ctx, ...args) => { clone.render = (ctx, ...args) => {
return ctx.mounted$ return ctx.mounted$
? h(Fragment, null, [h(_render(ctx, ...args), ctx.$attrs ?? ctx._.attrs)]) ? h(Fragment, ctx.$attrs ?? ctx._.attrs, component.render(ctx, ...args))
: h('div', ctx.$attrs ?? ctx._.attrs) : h('div', ctx.$attrs ?? ctx._.attrs)
} }
} else if (_template) { } else if (clone.template) {
// handle runtime-compiler template // handle runtime-compiler template
component.template = ` clone.template = `
<template v-if="mounted$">${_template}</template> <template v-if="mounted$">${component.template}</template>
<template v-else><div></div></template> <template v-else><div></div></template>
` `
} }
return defineComponent({
...component, clone.setup = (props, ctx) => {
setup (props, ctx) {
const mounted$ = ref(false) const mounted$ = ref(false)
onMounted(() => { mounted$.value = true }) onMounted(() => { mounted$.value = true })
return Promise.resolve(setup?.(props, ctx) || {}) return Promise.resolve(component.setup?.(props, ctx) || {})
.then((setupState) => { .then((setupState) => {
return typeof setupState !== 'function' return typeof setupState !== 'function'
? { ...setupState, mounted$ } ? { ...setupState, mounted$ }
: (...args) => { : (...args) => {
return mounted$.value return mounted$.value
// use Fragment to avoid oldChildren is null issue // use Fragment to avoid oldChildren is null issue
? h(Fragment, null, [h(setupState(...args), ctx.attrs)]) ? h(Fragment, ctx.attrs, setupState(...args))
: h('div', ctx.attrs) : h('div', ctx.attrs)
} }
}) })
} }
})
return clone
} }

View File

@ -68,20 +68,24 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => {
s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy)?([^'"]*?)["'][\s,]*\)/g, (full, lazy, name) => { s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy)?([^'"]*?)["'][\s,]*\)/g, (full, lazy, name) => {
const component = findComponent(components, name, options.mode) const component = findComponent(components, name, options.mode)
if (component) { if (component) {
const identifier = map.get(component) || `__nuxt_component_${num++}` let identifier = map.get(component) || `__nuxt_component_${num++}`
map.set(component, identifier) map.set(component, identifier)
if (lazy) {
imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }]))
identifier += '_lazy'
imports.add(`const ${identifier} = /*#__PURE__*/ __defineAsyncComponent(${genDynamicImport(component.filePath)})`)
} else {
imports.add(genImport(component.filePath, [{ name: component.export, as: identifier }]))
}
const isClientOnly = component.mode === 'client' const isClientOnly = component.mode === 'client'
if (isClientOnly) { if (isClientOnly) {
imports.add(genImport('#app/components/client-only', [{ name: 'createClientOnly' }])) imports.add(genImport('#app/components/client-only', [{ name: 'createClientOnly' }]))
imports.add(`const ${identifier}_client = /*#__PURE__*/ createClientOnly(${identifier})`)
identifier += '_client'
} }
if (lazy) { return identifier
imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }]))
imports.add(`const ${identifier}_lazy = /*#__PURE__*/ __defineAsyncComponent(${genDynamicImport(component.filePath)})`)
return isClientOnly ? `/*#__PURE__*/ createClientOnly(${identifier}_lazy)` : `${identifier}_lazy`
} else {
imports.add(genImport(component.filePath, [{ name: component.export, as: identifier }]))
return isClientOnly ? `/*#__PURE__*/ createClientOnly(${identifier})` : identifier
}
} }
// no matched // no matched
return full return full